Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 319
Narendra Vaghela Avatar asked Sep 07 '16 08:09

Narendra Vaghela


3 Answers

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.

like image 154
João Paulo Avatar answered Oct 28 '22 21:10

João Paulo


if you guys really stuck on this then go to the config/bootstrap.php file and add the following line and your issue is gone

header('Access-Control-Allow-Origin: *');
like image 30
Ashish Gaur Avatar answered Oct 28 '22 19:10

Ashish Gaur


For CakePHP 3.3+ version use this plugin : https://github.com/ozee31/cakephp-cors

like image 4
Ravi S. Singh Avatar answered Oct 28 '22 21:10

Ravi S. Singh