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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | <?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' ); // 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
1 2 3 4 5 6 7 8 9 10 11 12 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <?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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?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