Post Request working in Postman but returns Prefli

2019-02-28 11:17发布

问题:

THis is the error log from browser console XMLHttpRequest cannot load http://domain.com/xx/xxxxxxxx. Response for preflight has invalid HTTP status code 404

This is the expected response as received in Postman

{
  "status": "success",
  "code": "E012",
  "message": "Contact sent"
}

this is the request made from Angular 2

makeRequest(name, recipient) {
let body = JSON.stringify({
  "name": name,
  "recipient": recipient
});

let authToken = localStorage.getItem('token');
console.log(authToken);
let headers = new Headers({'Authorization': authToken , 'Content-Type': 'application/json'}); 
let options = new RequestOptions({headers: headers});
return this.http.post(url, body, options )
  .map(res => res.json());

}

Here is the behaviors function in Yii2

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

   // remove authentication filter
    $auth = $behaviors['authenticator'];
    unset($behaviors['authenticator']);

   // add CORS filter
    $behaviors['corsFilter'] = [
        'class' => Cors::className(),
        'cors' => [
            // restrict access to
            'Origin' => ['*'],
            'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
            // Allow only POST and PUT methods
            'Access-Control-Request-Headers' => ['*'],
            // Allow only headers 'X-Wsse'
            'Access-Control-Allow-Credentials' => true,
            // Allow OPTIONS caching
            'Access-Control-Max-Age' => 86400,
            // Allow the X-Pagination-Current-Page header to be exposed to the browser.
            'Access-Control-Expose-Headers' => [],
        ],
    ];

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

   return $behaviors;
}

Here is my UrlManager

   'urlManager' => [
        'enablePrettyUrl' => true,
        'enableStrictParsing' => true,
        'showScriptName' => false,
        'rules' => [
            'POST <version:[\w-]+>/sms' => '<version>/sms/send',
            'POST <version:[\w-]+>/users/verify' => '<version>/user/verify', 
            'POST <version:[\w-]+>/bulk' => '<version>/routine/index',
            'POST <version:[\w-]+>/contact' => '<version>/contact/index'],

回答1:

In certain cases a browser will automatically perform a preflight request to check the list of allowed methods or verbs before actually sending the reel one. You can see those within your browser's network tab. I guess in Postman you are directly sending the POST request while the pre-sent OPTIONS request should be the failing one.

Yii has a built-in action which is defined under the ActiveController class to respond to such requests. But in your case you are directly extending its parent controller instead so you'll need to manually define a similar action inside your controllers (or a parent one to them) responding to preflight requests:

public function actionOptions() 
{
    if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {
        Yii::$app->getResponse()->setStatusCode(405);
    }

    $allowed_verbs = ['GET', 'POST', 'HEAD', 'OPTIONS'];
    Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed_verbs));
}

Also; as you are not using the built-in routing mechanism for REST; in your case you'll also need to manually define rules to that Options action: (edited version of the code from your comments)

'urlManager' => [ 
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [ 
        'POST <version:[\w-]+>/users/verify' => '<version>/user/verify',
        'POST <version:[\w-]+>/airtime' => '<version>/airtime/airtime',
        'POST <version:[\w-]+>/bulk' => '<version>/routine/index',
        'POST <version:[\w-]+>/contact' => '<version>/contact/index',

        // OPTTIONS URI ENDPOINTS
        'OPTIONS <version:[\w-]+>/users/verify' => '<version>/user/options',
        'OPTIONS <version:[\w-]+>/airtime' => '<version>/airtime/options',
        'OPTIONS <version:[\w-]+>/bulk' => '<version>/routine/options',
        'OPTIONS <version:[\w-]+>/contact' => '<version>/contact/options',
    ],
];


回答2:

You should send body without JSON.stringify :

let body = {
  "name": name,
  "recipient": recipient
};

Good Luck