I have developed Angular & Yii2 REST service. Have problem in cross domain. Here below add my angular & Yii2 REST Code.
AngularJs : (like 'http://organization1.example.com','http://organization2.example.com',....)
$http.defaults.useXDomain = true;
$http.defaults.withCredentials = true;
$http.defaults.headers.common['Authorization'] = 'Bearer ' + MYTOKEN
My Request from Angular Controller:
apiURL = 'http://api.example.com';
$http.get(apiURL + '/roles')
.success(function (roles) { })
.error(function () { });
Yii2 .htaccess: (REST URL like 'http://api.example.com')
Header always set Access-Control-Allow-Origin: "*"
Header always set Access-Control-Allow-Credentials: true
Header always set Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "Authorization,X-Requested-With, content-type"
Yii2 My Behaviour:
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['corsFilter'] = [
'class' => Cors::className(),
'cors' => [
'Origin' => ['*'],
'Access-Control-Expose-Headers' => [
'X-Pagination-Per-Page',
'X-Pagination-Total-Count',
'X-Pagination-Current-Page',
'X-Pagination-Page-Count',
],
],
];
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['options'],
];
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
];
return $behaviors;
}
Problem
From my angular request is 'GET' method, but it will goes 'OPTIONS' method & return 401 Unauthorized error(CORS). because the request Authorization header is not send.
Update:
As pointed by @jlapoutre, this is now well described in official docs:
Adding the Cross-Origin Resource Sharing filter to a controller is a bit more complicated than adding other filters described above, because the CORS filter has to be applied before authentication methods and thus needs a slightly different approach compared to other filters. Also authentication has to be disabled for the CORS Preflight requests so that a browser can safely determine whether a request can be made beforehand without the need for sending authentication credentials. The following shows the code that is needed to add the yii\filters\Cors filter to an existing controller that extends from yii\rest\ActiveController:
use yii\filters\auth\HttpBasicAuth; public function behaviors() { $behaviors = parent::behaviors(); // remove authentication filter $auth = $behaviors['authenticator']; unset($behaviors['authenticator']); // add CORS filter $behaviors['corsFilter'] = [ 'class' => \yii\filters\Cors::className(), ]; // re-add authentication filter $behaviors['authenticator'] = $auth; // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method) $behaviors['authenticator']['except'] = ['options']; return $behaviors; }
Old Answer (deprecated)
There is an ordering issue when merging with parent::behaviors()
. Full details here.
I would recommend not defining keys when merging with parent array:
public function behaviors()
{
return \yii\helpers\ArrayHelper::merge([
[
'class' => \yii\filters\Cors::className(),
'cors' => [...],
],
[
'class' => \yii\filters\auth\HttpBearerAuth::className(),
'except' => ['options'],
],
[
'class' => ContentNegotiator::className(),
'formats' => [...],
]
], parent::behaviors());
}