У меня возникли проблемы со следующим действием «/ login» в моем классе UsersController
public function actionLogin(){ $data = Yii::$app->getRequest()->getBodyParams(); $model = new Usuario(); //Validamos que se hayan recibido los campos if(empty($data['email']) || empty($data['password'])){ throw new \yii\web\BadRequestHttpException("Debe ingresar email y password"); } //Validamos usuario y contraseña $usuario = $model->findByUsername($data['email']); if(empty($usuario) || !$usuario->validatePassword($data['password'])){ throw new \yii\web\UnauthorizedHttpException("Usuario y/o contraseña incorrectos"); } return $usuario; }
Ситуация заключается в том, что я использую метод POST для входа в систему, и я вызываю этот маршрут из другого домена, поэтому передняя библиотека сначала пытается вызвать / войти в маршрут с помощью метода OPTIONS, чтобы проверить, разрешено или не разрешено / Войти с POST ..
Проблема в том, что встроенные функции yii2 останутся в ActiveController только для / users и / users / {id}
Если я вручную добавлю этот / логин, чтобы быть доступным как в POST, так и в OPTIONS через действия verbFilter, тогда yii пытается фактически вызвать действие входа с запросом OPTIONS. Я имею в виду, он пытается выполнить вход в систему. Конечно, это невозможно, поскольку он не отправляет поля электронной почты и паролей, но я вижу ошибку в файле журнала.
Итак, мой вопрос: … Есть ли способ правильно настроить эти «настраиваемые» маршруты и сделать OPTIONS прозрачным? Поскольку я ожидаю, что действие входа не будет выполнено при вызове с помощью OPTIONS, но вместо этого вернет непосредственно заголовки методов, разрешенных OPTIONS.
Обновить информацию: Добавлены правила менеджера URL
'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => true, 'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => ['v1/users'], 'pluralize' => false, 'tokens' => [ '{id}' => '<id:\\w+>' ] ], //Rutas usuario 'v1/login' => '/v1/users/login' ], ],
По умолчанию класс yii\rest\UrlRule
будет применять эти шаблоны к любой конечной точке:
'patterns' => [ 'PUT,PATCH {id}' => 'update', 'DELETE {id}' => 'delete', 'GET,HEAD {id}' => 'view', 'POST' => 'create', 'GET,HEAD' => 'index', '{id}' => 'options', '' => 'options', ]
Это означает, что любой запрос, содержащий команды OPTIONS, будет перенаправлен на yii\rest\OptionsAction
если ваш loginAction
написан внутри класса, расширяющего ActiveController .
Я предлагаю переопределить patterns
, оставив единственные используемые глаголы, поскольку ваше действие входа не требует каких-либо других действий CRUD. это должно работать с вашим делом:
'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => ['v1/users'], 'pluralize' => false, 'tokens' => [ '{id}' => '<id:\\w+>' ] ], [ 'class' => 'yii\rest\UrlRule', 'controller' => ['v1/login' => '/v1/users/login'], 'patterns' => [ 'POST' => 'login', '' => 'options', ] ] ],
ПРИМЕЧАНИЕ. Решение @CreatoR также является требованием здесь и точно так же, как и он, без определения ключей. иначе аргументы OPTIONS будут отклонены, если не будут аутентифицированы.
В случае, если ваше действие для входа определено под классом, расширяющим yii\rest\Controller
напрямую, а не через yii\rest\ActiveController
(что должно быть подходящим для действий аутентификации, поскольку здесь нет CRUD), то те же правила config должны работать отлично, но вам нужно вручную добавить actionOptions в свой код:
// grabbed from yii\rest\OptionsAction with a little work around private $_verbs = ['POST','OPTIONS']; public function actionOptions () { if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') { Yii::$app->getResponse()->setStatusCode(405); } $options = $this->_verbs; Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $options)); }
Я решил проблему, расширив класс фильтра Cors:
use Yii; use yii\filters\Cors; class CorsCustom extends Cors { public function beforeAction($action) { parent::beforeAction($action); if (Yii::$app->getRequest()->getMethod() === 'OPTIONS') { Yii::$app->getResponse()->getHeaders()->set('Allow', 'POST GET PUT'); Yii::$app->end(); } return true; } }
а потом
public function behaviors() { $behaviors = parent::behaviors(); unset($behaviors['authenticator']); $behaviors['corsFilter'] = [ 'class' => CorsCustom::className(), ]; $behaviors['authenticator'] = [ 'class' => HttpBearerAuth::className(), 'optional' => ['login'] ]; return $behaviors; }
неpublic function behaviors() { $behaviors = parent::behaviors(); unset($behaviors['authenticator']); $behaviors['corsFilter'] = [ 'class' => CorsCustom::className(), ]; $behaviors['authenticator'] = [ 'class' => HttpBearerAuth::className(), 'optional' => ['login'] ]; return $behaviors; }
У меня была такая же проблема.
Вот как я это исправил:
Я добавил параметр extraPatterns
для своего правила, например:
'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'pluralize' => false, 'controller' => [ 'athlete', 'admin', 'address', 'program' ], 'extraPatterns' => [ 'OPTIONS <action:\w+>' => 'options' ] ] ], ],
Таким образом, действие options
будет вызываться для каждого пользовательского действия, которое у меня есть в любом из этих контроллеров.
Вам необходимо присоединить фильтр Cors
в методе behaviours()
вашего контроллера behaviours()
(см. Как в официальном руководстве ) со следующими условиями:
Cors
должен быть определен до фильтров аутентификации / авторизации AccessControl
В вашей ситуации у UsersController
может быть такое behaviors()
:
public function behaviors() { return ArrayHelper::merge( [ 'cors' => [ 'class' => Cors::className(), ], ], parent::behaviors(), [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ ['allow' => true, 'actions' => ['options']], ] ], ] ); }
'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => ['v1/users'], 'pluralize' => false, 'tokens' => [ '{id}' => '<id:\\w+>' ] ], [ 'class' => 'yii\rest\UrlRule', 'controller' => ['v1/login' => '/v1/users/login'], 'patterns' => [ 'POST' => 'login', 'OPTIONS' => 'options', ] ] ],