yii2 restful api: (Reason: CORS header ‘Access-Con

2019-09-03 05:37发布

问题:

I want use React with Yii2 RESTful, i created a users controller like this:

<?php
namespace app\controllers;
use yii\rest\ActiveController;

class UsersController extends ActiveController
{
    public $modelClass = 'app\models\User';
}

when open link in browser show my users, when i want use axios in react, i get error i browser console:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/rest/web/users. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

but when i check network in firefox developer tools, i find axios request and it status in 200 and recievies the response correctly.

i try use behaviors function in my controllers, like this:

public function behaviors()
{
    return [
        'corsFilter' => [
            'class' => \yii\filters\Cors::className(),
            'cors' => [
                'Origin' => ['*'],
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
                'Access-Control-Request-Headers' => ['*'],
            ],

        ],
    ];
}

but get error

Invalid Argument – yii\base\InvalidArgumentException Response content must not be an array.

how i can fix this?

回答1:

Update

Updated the answer as the logic implemented was allowing every request to by pass the authentication filters (Thanks to @KalyanHalderRaaz for pointing out the bug).

There are two things to change

  • When re-adding the filters it is better to specify which auth you are using. change the code below

    // re-add authentication filter
    $behaviors['authenticator'] = $auth;
    

    to the following, i am using BasicAuth for example.

    $behaviors['authenticator'] = [
        'class' => yii\filters\auth\HttpBasicAuth::class
    ];
    
  • When adding a beforeAction() dont forget to wrap the logic in if(parent::beforeAction($action)) otherwise it would authenticate every request as we are just returning true for every request here and also not calling the parent which would trigger the filters.

    Replace the beforeAction() with the following

    public function beforeAction($action)
    {
        if (parent::beforeAction($action)) {
            \Yii::$app->response->format = Response::FORMAT_JSON;
            return true;
        }
    
    }
    

Just make sure you are overriding the findIdentityByAccessToken() in the User identity model


According to docs you should first unset the authenticator filter in order to add the Cors filter, so your behavior should look like

public function behaviors() {
    $behaviors = parent::behaviors();

    // remove authentication filter necessary because we need to 
    // add CORS filter and it should be added after the CORS
    unset($behaviors['authenticator']);

    // add CORS filter
    $behaviors['corsFilter'] = [
        'class' => '\yii\filters\Cors',
        'cors' => [
            'Origin' => ['*'],
            'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
            'Access-Control-Request-Headers' => ['*'],
        ],
    ];

    // re-add authentication filter of your choce
    $behaviors['authenticator'] = [
        'class' => yii\filters\auth\HttpBasicAuth::class
    ];

    // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
    $behaviors['authenticator']['except'] = ['options'];
    return $behaviors;
}

And you can set the response format to json inside your controller by adding the beforeAction like below

public function beforeAction($action)
{
    if (parent::beforeAction($action)) {
        \Yii::$app->response->format = Response::FORMAT_JSON;
        return true;
    }

}