Before fetch
sends a POST request to another domain it will do a CORS pre-flight check. The following website has the HTTP OPTIONS middleware code you can use for CakePHP 3. It mentions for a CakePHP 2 version you need to go to another website which is unavailable now (July 2019). See below for my CakePHP 2 code do do the same thing.
CakePHP 2.x DispatchFilter CORS 'OPTIONS' pre-flight check response code
This basically says if the request is an OPTIONS check is from the right origin and the requested method is allowed respond with the right headers.
<?php
/**
* CakePHP 2 DispatchFilter code for CORS OPTIONS pre-flight check
* fetch sends an OPTIONS pre-flight check
* copy this file as OptionsFilter.php to app/Routing/Filter/
* load this DispatcherFilter by finding this block of code in
* app/Config/bootstrap.php and appending the
* 'OptionsFilter' entry
* Configure::write('Dispatcher.filters', [
* 'AssetDispatcher',
* 'CacheDispatcher',
* 'OptionsFilter' // custom by tgn
* ]);
*
*/
App::uses('DispatcherFilter', 'Routing');
App::uses('Configure', 'Core');
class OptionsFilter extends DispatcherFilter
{
/**
* @var int
*/
public $priority = 9;
/**
* @param CakeEvent $event
* @return mixed
*/
public function beforeDispatch(CakeEvent $event)
{
$request = $event->data['request'];
$response = $event->data['response'];
// here is your allowed origins
// I set them using Configure in bootstrap.php
// Configure::write('ALLOWED_METHODS', ['PUT', 'POST', 'DELETE']);
// Configure::write('ALLOWED_ORIGINS',['http://localhost:3000', 'http://localhost:8081']);
$allowedOrigins = Configure::read('ALLOWED_ORIGINS');
// here are the allowed methods
$allowedMethods = Configure::read('ALLOWED_METHODS');
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
// 2 hours
$accessControlMaxAge = '7200';
if ($request->is('OPTIONS')) {
// where is the request coming from
// e.g. from my react dev environment
// it is http://localhost:3000
$origin = $request->header('Origin');
// what method are they requesting to use
$method = $request->header('Access-Control-Request-Method');
// what headers are they sending
$headers = $request->header('Access-Control-Request-Headers');
// if allow then set headers
if (in_array($origin, $allowedOrigins)) {
$response->header(
'Access-Control-Allow-Origin',
$origin
);
}
if (in_array($method, $allowedMethods)) {
$response->header(
"Access-Control-Allow-Methods", $method
);
}
$response->header(
"Access-Control-Allow-Headers",
$headers
);
$response->header('Access-Control-Allow-Credentials', "true");
$response->header('Access-Control-Max-Age', $accessControlMaxAge);
$response->header('Content-Type', 'application/json');
// this is just me goofing off
// but this response viewed in the dev tools of the
// browsers lets me know
// I have triggered the DispatchFilter
$response->body(
json_encode(
[
'options' => 'pre-flight from dispatcher'
]
)
);
$event->stopPropagation();
return $response;
}
}
}
Code to put in CakePHP 2.x Actions to allow POST
public function add($shipment_type = null)
{
$last = null;
$error = null;
$origin = $this->request->header('Origin');
$allowedOrigins = Configure::read('ALLOWED_ORIGINS');
if (in_array($origin, $allowedOrigins)) {
$this->response->header('Access-Control-Allow-Origin', $origin);
}
// rest of your action code
Chrome DevTools View of the OPTIONS pre-flight
Response from the CakePHP 2.x DispatcherFilter code above
Note the OPTIONS Request Headers are Access-Control-Request-Headers: content-type, x-requested-with, Access-Control-Request-Method: POST and Origin: http://localhost:3000
The dispatch filter sends the following headers as response
- Access-Control-Allow-Credentials: true
- Access-Control-Allow-Headers: content-type,x-requested-with
- Access-Control-Allow-Methods: POST
- Access-Control-Allow-Origin: http://localhost:3000
- Access-Control-Max-Age: 7200
You may have seen 'Access-Control-Allow-Origin' set to '*'
POST request after receiving the correct response headers
This is the fetch code in a react App that generates the OPTIONS and POST request.
fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, cors, *same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, *same-origin, omit
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest"
},
redirect: "error", // manual, *follow, error
//referrer: "no-referrer", // no-referrer, *client
body: JSON.stringify(postObject) // body data type must match "Content-Type" header
}).then(response => response.json());
CakePHP 3 Options Middleware Code
Just in case the original website goes away
<?php
namespace App\Middleware;
class HttpOptionsMiddleware
{
public function __invoke($request, $response, $next)
{
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
if ($request->getMethod() == 'OPTIONS')
{
$method = $request->getHeader('Access-Control-Request-Method');
$headers = $request->getHeader('Access-Control-Request-Headers');
$allowed = empty($method) ? 'GET, POST, PUT, DELETE' : $method;
$response = $response
->withHeader('Access-Control-Allow-Headers', $headers)
->withHeader('Access-Control-Allow-Methods', $allowed)
->withHeader('Access-Control-Allow-Credentials', 'true')
->withHeader('Access-Control-Max-Age', '86400');
return $response;
}
return $next($request, $response);
}
}
Register Middleware in src/Application for CakePHP 3.x
To install the CakePHP 3 httpOptionsMiddleware
mkdir src/Middleware
create a new file named src/Middleware/HttpOptionsMiddleware.php and put the contents of the below code into it
Open src/Application.php include a use statement for the code and append the new hook code for the middleware:
<?php
use App\Middleware\HttpOptionsMiddleware;
class Application extends BaseApplication {
public function middleware($middlewareQueue)
{
$middlewareQueue
// Add routing middleware.
->add(new RoutingMiddleware($this))
//... other middleware etc
// add http options
->add( new HttpOptionsMiddleware($this));
return $middlewareQueue;
}
0 Comments