flightphp / core

An extensible micro-framework for PHP
https://docs.flightphp.com
MIT License
2.61k stars 408 forks source link

Does the framework support CORS by default? #293

Closed lrcry closed 7 years ago

lrcry commented 7 years ago

CORS (Cross Origin Resource Sharing) can be useful in both development and production environment. While it seems that the framework does not provide support on CORS by default. I indeed got problems when our front-end dev guys asked me to open a CORS for them to test the API on dev environments themselves.

I did the following workaround:

Flight::route('OPTIONS /users/*', function() {
        Flight::json('Anyway, return something for OPTIONS requests');
});

And it worked.

That was pretty much the same as what I did several times in my Java Spring projects, which allowed me to config CORS by routes.

Just wondering if there are any better approaches for doing this?

ghost commented 7 years ago

Hi @lrcry, I've taken a look at this and I think I understand the issue (putting aside the none-issue with you not having a route set up to listen to OPTIONS /users/*.) By default, Flight has full support for CORS as it doesn't impose any CORS restrictions beyond what the underlying server provides.

If you want to enable CORS in a route by route way, you're going to have to refer to the Apache or NGINX documentation.

Current state

I'm going to assume that currently your CORS requests are working because you're responding with the header Access-Control-Allow-Origin set to * or the domain you're receiving requests from.

Enabling CORS for a given path in Apache

To enable CORS for the /users path (and its children) in Apache, add the following to your .htaccess file:

<Location "/users">
     Header set Access-Control-Allow-Origin "www.puttherequestingdomainhere.com"
</Location>

Enabling CORS for a given path in NGINX

To enable CORS for the /users path (and its children) in NGINX, add the following to the relevant .conf file in /etc/nginx/sites-available:

location /users {
    add_header Access-Control-Allow-Origin www.puttherequestingdomainhere.com;
}

Please be aware that in order to use this within NGINX, your NGINX installation has to be compiled with the ngx_http_headers_module package: http://nginx.org/en/docs/http/ngx_http_headers_module.html

lrcry commented 7 years ago

Hey @shanelja mate,

Thanks for your idea here!

Basically, I did what you said in your latest reply with Apache2 before you posted it.

Check if the server enabled CORS. I did this by requesting via Postman to my API on server and found it was not enabled. I added support in my .htaccess file.

After this, I checked with a request via Postman, and I could see the header was set. It worked pretty well with Postman. I even tested it by writing a simple client of Android, it also worked. But you know, a common situation is that we need to use it to feed a JavaScript-based webapp.

Unfortunately, when I tried jQuery Ajax on it, the OPTIONS /api/somewhere 404 not found error occurred every time.

I am not really sure if this is caused by CORS, but it seems jQuery Ajax will first do an OPTIONS request, then the actual POST/PUT/DELETE request. I think in my case, it is because of my route index.php lack of items for OPTIONS.

ghost commented 7 years ago

@lrcry - Ahhh, thanks for clarifying - you're correct though, I believe this is caused by the browsers "preflight" request, a request made to OPTIONS /url/ before the GET/PUT/POST/DELETE /url/ request.

Typically, this is looking for a Access-Control-Request-Methods header which defines which HTTP methods may be used to access the requested resource, as well as to check whether or not CORS is enabled.

Your solution works correctly, setting a route for the OPTIONS method will cause the preflight request to return successfully instead of throwing a 404 - I don't think this warrants a framework change, but I do think that the Method Routing section of the help site (http://flightphp.com/learn/#method-routing) could use an addendum informing users that to enable CORS requests within browser runtimes, an OPTIONS /path/to/resource route should be specified.

@mikecao what are your thoughts on this? I've added a pull request for the flightphp.com repository below which addresses this.

Some useful reading: HTTP OPTIONS: http://zacstewart.com/2012/04/14/http-options-method.html Browser ajax preflight requests: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

For future, incase the URLs are no longer returning a resource:

Unlike simple requests (discussed above), "preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:

It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)