Yii2 Rest - 自定义动作和OPTIONS方法

时间:2015-05-25 23:32:52

标签: php rest yii2

我在执行以下操作时出现问题" / 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方法调用/ login route来检查是否允许或不允许使用POST调用/登录..

问题是yii2 rest ActiveController的内置功能仅适用于/ users和/ users / {id}

如果我通过动作verbFilter手动添加此/登录路由以在POST和OPTIONS中都可用,则yii尝试使用OPTIONS请求实际调用登录操作。我的意思是,它正在尝试登录。当然它不能,因为它不发送电子邮件和密码字段,但我可以在日志文件中看到错误。

所以,我的问题是......有没有办法正确配置这个" custom"路由操作并使OPTIONS透明地执行?因为我希望在使用OPTIONS调用它时不会执行登录操作,而是直接返回OPTIONS允许的方法标题。

更新信息:添加了网址管理员规则

'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'
        ],        
    ],

5 个答案:

答案 0 :(得分:7)

默认情况下,yii\rest\UrlRule类会将这些模式应用于任何端点:

'patterns' => [
    'PUT,PATCH {id}' => 'update',
    'DELETE {id}' => 'delete',
    'GET,HEAD {id}' => 'view',
    'POST' => 'create',
    'GET,HEAD' => 'index',
    '{id}' => 'options',
    '' => 'options',
]

这意味着如果您的yii\rest\OptionsAction写在扩展 ActiveController 的类中,则任何包含OPTIONS谓词的请求都将被重定向到loginAction

我建议通过留下唯一使用的动词来覆盖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这里需要)然后相同的规则配置应该可以正常工作,但您需要手动将 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));
}

答案 1 :(得分:4)

我有同样的问题。

这就是我修复它的方法:

我在规则中添加了extraPatterns参数,如下所示:

    'urlManager' => [
        'enablePrettyUrl' => true,
        'showScriptName' => false,
        'rules' => [
            [
                'class' => 'yii\rest\UrlRule',
                'pluralize' => false,
                'controller' => [
                    'athlete',
                    'admin',
                    'address',
                    'program'
                ],
                'extraPatterns' => [
                    'OPTIONS <action:\w+>' => 'options'
                ]
            ]
        ],
    ],

这样,我将在任何这些控制器中为每个自定义操作调用options操作。

答案 2 :(得分:3)

我通过扩展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;
}

答案 3 :(得分:0)

您需要在控制器的Cors方法中附加behaviours()过滤器(请参阅in official guide)以下条件:

    应在身份验证/授权过滤器之前定义
  1. Cors过滤器
  2. 允许所有用户在option过滤器
  3. 中对AccessControl进行操作

    在您的情况下,UsersController可能有behaviors()方法:

    public function behaviors()
    {
        return ArrayHelper::merge(
            [
                'cors' => [
                    'class' => Cors::className(),
                ],
            ],
            parent::behaviors(),
            [
                'access' => [
                    'class' => AccessControl::className(),
                    'rules' => [
                         ['allow' => true, 'actions' => ['options']],
                    ]
                ],
            ]
        );
    }
    

答案 4 :(得分:0)

'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',
        ]
    ] 
],