Reputation: 237
I built an API with the SLIM Micro-Framework. I setup some middleware that adds the CORS headers using the following code.
class Cors{
public function __invoke(Request $request, Response $response, $next){
$response = $next($request, $response);
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}
}
For my front-end, I used VueJS. I setup VueResource and created a function with the following code.
register (context, email, password) {
Vue.http({
url: 'api/auth/register',
method: 'POST',
data: {
email: email,
password: password
}
}).then(response => {
context.success = true
}, response => {
context.response = response.data
context.error = true
})
}
In chrome, the following error is logged to the console.
XMLHttpRequest cannot load http://mysite:9800/api/auth/register. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://mysite' is therefore not allowed access.
Oddly enough, GET requests work perfectly.
Upvotes: 5
Views: 21626
Reputation: 2625
You half 1/2 the solution here.
What you are missing is an OPTIONS route where these headers need to be added as well.
$app->options('/{routes:.+}', function ($request, $response, $args) {
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
});
Upvotes: 3
Reputation: 12339
CORS can be hard to config. The key is that you need to set the special headers in your server and your client, and I don't see any Vue headers set, besides as far as I know http
is not a function. However here is some setup for a post request.
const data = {
email: email,
password: password
}
const options = {
headers: {
'Access-Control-Expose-Headers': // all of your headers,
'Access-Control-Allow-Origin': '*'
}
}
Vue.http.post('api/auth/register', JSON.stringify(data), options).then(response => {
// success
}, response => {
// error
})
Notice that you need to stringify your data and you need to expose your headers, usually including the Access-Control-Allow-Origin
header.
What I did in one of my own apps was to define interceptors so I don't worry to set headers for every request.
Vue.http.headers.common['Access-Control-Expose-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, x-session-token, timeout, Content-Length, location, *'
Vue.http.headers.common['Access-Control-Allow-Origin'] = '*'
Upvotes: 1
Reputation: 10720
Actually CORS is implemented at browser level. and Even with
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
chrome and Mozilla will not set headers to allow cross origin. So, you need forcefully disable that..
Read more about disabling CORS
Upvotes: 1
Reputation: 329
This happens because preflight request is of OPTIONS type. You need to make an event listener on your request, which checks the type and sends a response with needed headers.
Unfortunately i don't know Slim framework, but here's the working example in Symfony.
First the headers example to be returned:
// Headers allowed to be returned.
const ALLOWED_HEADERS = ['Authorization', 'Origin', 'Content-Type', 'Content-Length', 'Accept'];
And in the request listener, there's a onKernelRequest method that watches all requests that are coming in:
/**
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
// Don't do anything if it's not the master request
if (!$event->isMasterRequest()) {
return;
}
// Catch all pre-request events
if ($event->getRequest()->isMethod('OPTIONS')) {
$router = $this->container->get('router');
$pathInfo = $event->getRequest()->getPathInfo();
$response = new Response();
$response->headers->set('Access-Control-Allow-Origin', $event->getRequest()->headers->get('Origin'));
$response->headers->set('Access-Control-Allow-Methods', $this->getAllowedMethods($router, $pathInfo));
$response->headers->set('Access-Control-Allow-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Expose-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Max-Age', 60 * 60 * 24);
$response->send();
}
}
Here i just reproduce the Origin (all domains are allowed to request the resource, you should probably change it to your domain). Hope it will give some glues.
Upvotes: 1