API: проблема с модулем

Всё что касается построения API
Ответить
SindBad
Сообщения: 81
Зарегистрирован: 2015.06.18, 10:53

API: проблема с модулем

Сообщение SindBad »

Привет всем!
Пытаюсь создать API посредством модуля, но что-то не выходит, не работает ничего "из коробки", как ожидалось.
Текущая цель: получение стран из таблицы country посредством модели common\models\Country.
В качестве руководства взял Быстрый старт .
Итак, создаю модуль, в нем прописываю конфигурацию:

Код: Выделить всё

<?php

namespace frontend\modules\api;

class Module extends \yii\base\Module
{
    public $controllerNamespace = 'frontend\modules\api\controllers';

    public function init()
    {
        //curl -i -H "Accept:application/json" "http://local/api/countries/"
        //curl -i -H "Accept:application/json" "http://local/api/countries/63"
        parent::init();
        \Yii::configure($this, [
            'on beforeAction' => function(){}, //должен был "перебить" beforeAction из глобальной конфигурации, но не перебивает :(
            'components' => [
                'urlManager' => [
                    'class'                 => 'yii\web\UrlManager', 
                    'enablePrettyUrl'       => true,
                    'enableStrictParsing'   => true,
                    'showScriptName'        => false,
                    'rules'                 => [
                         [
                            'class'      => 'yii\rest\UrlRule', 
                            'controller' => 'countries',
                            'pluralize'  => false, //запретил - было подозрение, что проблема в этом.
                        ],
                    ],
                ],
            ],
        ]);
    }
}
Затем добавляю контроллер:

Код: Выделить всё

<?php

namespace frontend\modules\api\controllers;

use yii\web\Controller;
use yii\rest\ActiveController;

class CountriesController extends ActiveController
{
    public $modelClass = 'common\models\Country';
}
Итак, тестирую http://local/api/countries/ - выводится список, разбитый на странички. Для браузера - XML, для curl - JSON.

Код: Выделить всё

[{"country_id":"4","city_id":0,"name":"Австралия"},{"country_id":"63","city_id":0,"name":"Австрия"},{"country_id":"81","city_id":0,"name":"Азербайджан"},{"country_id":"173","city_id":0,"name":"Ангуилья"},{"country_id":"177","city_id":0,"name":"Аргентина"},{"country_id":"245","city_id":0,"name":"Армения"},{"country_id":"248","city_id":0,"name":"Беларусь"},{"country_id":"401","city_id":0,"name":"Белиз"},{"country_id":"404","city_id":0,"name":"Бельгия"},{"country_id":"425","city_id":0,"name":"Бермуды"},{"country_id":"428","city_id":0,"name":"Болгария"},{"country_id":"467","city_id":0,"name":"Бразилия"},{"country_id":"616","city_id":0,"name":"Великобритания"},{"country_id":"924","city_id":0,"name":"Венгрия"},{"country_id":"971","city_id":0,"name":"Вьетнам"},{"country_id":"994","city_id":0,"name":"Гаити"},{"country_id":"1007","city_id":0,"name":"Гваделупа"},{"country_id":"1012","city_id":0,"name":"Германия"},{"country_id":"1206","city_id":0,"name":"Голландия"},{"country_id":"1258","city_id":0,"name":"Греция"}]
Ну вроде бы всё прекрасно. Ан нет...
http://nh/api/countries/63 - 404, страница не найдена. Причем, как ни старался, выводится либо исключение голым текстом, либо полный срач с html-тегами. Делал, как описано здесь:
Идея состояла в том, чтобы формировать ответ с Exception’ом в виде массива, а далее REST-контроллер сам отформатирует это в JSON (такова логика работы REST-контроллеров в Yii2 “из коробки”).
Нифига REST-контроллер сам не отформатировал. Может у меня "коробка" неправильная...
И так с любыми данными - общий список - ок, остальное - 404.

Пока единственный код ответа, который выводится как надо - это 400 в ответ на http://nh/api/countries/view:

Код: Выделить всё

{"name":"Bad Request","message":"Отсутствуют обязательные параметры: id","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"}
(без участия кастомного обработчика ошибок).

В чём моя ошибка? Как правильно использовать все возможности REST в модуле? И как в модуле перебить beforeAction, заданный в приложении?

UPD: судя по всему, Yii::configure($this, [...]); здесь вообще не работает.

UPD2: Попробовал Adding Rules Dynamically. Параметры начинают работать, но enableStrictParsing ломает сразу всё приложение (увы, bootstrap оказывает глобальное влияние на настройки). Видимо, моя идея с API на модуле была изначально дохлой.
Ailme
Сообщения: 9
Зарегистрирован: 2009.09.02, 13:24
Контактная информация:

Re: API: проблема с модулем

Сообщение Ailme »

Вчера то же столкнулся с этой проблемой. Изучение исходников показало, что yii\rest\UrlRule не предназначен для модулей, поэтому пришлось самостоятельно настраивать роутинг для модуля.

Пример получившегося Module.php для модуля:

Код: Выделить всё

<?php

namespace app\modules\api;

use Yii;
use yii\base\Module as BaseModule;
use yii\web\GroupUrlRule;

/**
 * Class Module
 *
 * @package app\modules\api
 */
class Module extends BaseModule
{
    const VERSION = '0.0.2';

    public $controllerNamespace = 'app\modules\api\controllers';

    /**
     * @var string The prefix for user module URL.
     *
     * @See [[GroupUrlRule::prefix]]
     */
    public $urlPrefix = 'api';

    /** @var array The rules to be used in URL management. */
    public $urlRules = [
        [
            'verb' => ['PUT', 'PATCH'],
            'pattern' => '<controller:[\w\-]+>/<id:\w+>',
            'route' => '<controller>/update',
        ],
        [
            'verb' => ['DELETE'],
            'pattern' => '<controller:[\w\-]+>/<id:\w+>',
            'route' => '<controller>/delete',
        ],
        [
            'verb' => ['GET', 'HEAD'],
            'pattern' => '<controller:[\w\-]+>/<id:\d+>',
            'route' => '<controller>/view',
        ],
        [
            'verb' => ['POST'],
            'pattern' => '<controller:[\w\-]+>',
            'route' => '<controller>create',
        ],
        [
            'verb' => ['GET', 'HEAD'],
            'pattern' => '<controller:[\w\-]+>',
            'route' => '<controller>/index',
        ],
        // это уже своё, чтобы можно было и другие экшены вызывать
        [
            'verb' => ['GET', 'HEAD'],
            'pattern' => '<controller:[\w\-]+>/<action:[\w\-]+>',
            'route' => '<controller>/<action>',
        ],
    ];

    public function init()
    {
        parent::init();

        if (Yii::$app instanceof \yii\console\Application) {
            $this->controllerNamespace = 'app\modules\api\commands';
        }

        Yii::$app->urlManager->addRules([
            new GroupUrlRule([
                "prefix" => $this->urlPrefix,
                "rules" => $this->urlRules,
            ]),
        ], false);
    }
}

 
в конфиге приложения нужно дописать:

Код: Выделить всё

...
    'bootstrap' => ['log', 'api'],
...
    'modules' => [
...
        'api' => [
            'class' => 'app\modules\api\Module',
        ],
    ],
 
Аватара пользователя
matios
Сообщения: 76
Зарегистрирован: 2013.03.17, 20:44
Откуда: Россия, Лиски

Re: API: проблема с модулем

Сообщение matios »

Столкнулся с той же проблемой. Рецепт от Ailme работает.
Но все же интересно, yii\rest\UrlRule до сих пор не адаптировали под модули?

Так как такие варианты не работают (выдает 404):

Код: Выделить всё

[
    'class' => 'yii\rest\UrlRule',
    'controller' => api\modules\v1\controllers\UserController::class
]

Код: Выделить всё

[
    'class' => 'yii\rest\UrlRule', 
    'controller' => 'v1/user', 
]
maxtor
Сообщения: 78
Зарегистрирован: 2011.12.06, 11:04

Re: API: проблема с модулем

Сообщение maxtor »

Делаю, все как написал Ailme. Но все равно получаю 404.
enableStrictParsing нужно устанавливать в true?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: API: проблема с модулем

Сообщение zelenin »

ну что значит под модули не адаптировано, если даже в документации уже два года про модули написано?
https://github.com/yiisoft/yii2/blob/ma ... sioning.md
maxtor
Сообщения: 78
Зарегистрирован: 2011.12.06, 11:04

Re: API: проблема с модулем

Сообщение maxtor »

Моя проблема была в том, что был криво переопределен behaviors у контроллера. Теперь все работает.
Ответить