简体   繁体   中英

CakePHP 3 REST API + CORS Request and OPTIONS method

I am working on a REST API using CakePHP 3. I want to enable it publicly, so anyone can mane a call to API. So, I have added cors headers as defined here: http://book.cakephp.org/3.0/en/controllers/request-response.html#setting-cross-origin-request-headers-cors

I have implemented the EventListener on Dispatcher.beforeDispatch and Dispatcher.beforeDispatch to prepare the cors headers.

class ApiResponseHeaders implements EventListenerInterface
{

    /**
     * Event bindings
     *
     * @return array
     */
    public function implementedEvents()
    {
        return [
            'Dispatcher.beforeDispatch' => [
                'callable' => 'beforeDispatch',
                'priority' => 0
            ],
            'Dispatcher.afterDispatch' => [
                'callable' => 'afterDispatch',
                'priority' => 99999
            ]
        ];
    }

    public function beforeDispatch(Event $event)
    {
        $request = $event->data['request'];
        if ('OPTIONS' === $request->method()) {
            $event->stopPropagation();
        }
    }

    public function afterDispatch(Event $event)
    {
        $request = $event->data['request'];
        $response = $event->data['response'];

        $response->cors($request)
                ->allowOrigin('*')
                ->allowMethods(['GET', 'POST', 'OPTIONS'])
                ->allowHeaders(['Content-Type, Authorization, X-Requested-With, Accept'])
                ->allowCredentials()
                ->maxAge(0)
                ->build();

        if ('OPTIONS' === $request->method()) {
            $event->stopPropagation();
        }
    }

}

But the problem is, when I make an POST request to API endpoint from AngularJS, first it makes OPTIONS method call and then POST method. CakePHP does not process the OPTIONS method call and returns: 400 Bad Request

Below are the sample request and response headers:

Request headers

OPTIONS /v1/account/login HTTP/1.1
Host: api.cake.dev
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://demo.angular.dev
Connection: keep-alive

Response headers

HTTP/1.1 400 Bad Request
Date: Wed, 07 Sep 2016 08:34:55 GMT
Server: Apache/2.4.17 (Win64) PHP/5.6.16
X-Powered-By: PHP/5.6.16
X-DEBUGKIT-ID: b4e5654a-cefb-43b7-bf19-4e8c7ffdb0e0
access-control-allow-origin: *
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-headers: Content-Type, Authorization, X-Requested-With, Accept
Access-Control-Allow-Credentials: true
access-control-max-age: 0
Content-Length: 147
Connection: close
Content-Type: text/html; charset=UTF-8

Is there a way available in CakePHP 3 to handle this OPTIONS request and return correct response so than the next POST request work correctly?

Please help. Thank you

With CakePHP 3.2+, I did in this way:

public function beforeRender(event $event) {
    $this->setCorsHeaders();
}

public function beforeFilter(event $event) {
    if ($this->request->is('options')) {
        $this->setCorsHeaders();
        return $this->response;
    }
}

private function setCorsHeaders() {
    $this->response = $this->response->cors($this->request)
        ->allowOrigin(['*'])
        ->allowMethods(['*'])
        ->allowHeaders(['x-xsrf-token', 'Origin', 'Content-Type', 'X-Auth-Token'])
        ->allowCredentials(['true'])
        ->exposeHeaders(['Link'])
        ->maxAge(300)
        ->build();
}

In this way, I can even handle CakePHP error pages.

Depending on your case, you can change values on allowHeaders array.

Remembering that if the request has withCredentials set to true , you will need to specify the origins.

对于 CakePHP 3.3+ 版本,请使用此插件: https : //github.com/ozee31/cakephp-cors

如果你们真的坚持这个然后去config/bootstrap.php文件并添加以下行,你的问题就消失了

header('Access-Control-Allow-Origin: *');

in your appController place the following:

use Cake\Event\Event; //if you dont have this allready

public function beforeFilter(event $event) { //if you dont have this beforeFilter already
    if ($this->request->is('options')) {
        return $this->response;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM