Understanding Cross-Origin-Resource-Sharing (CORS) by example

If you’re here because you’re struggling with this:

CORS with jQuery and ninjaframework

The solution can be summarized in the following steps:


1. The server application must be configured to specifically (or generically) accept requests from the client domain.

2. The server application must specifically accept any headers included in the request.

3. The HTTP method type issued by the client must obviously be accepted by the server application but so too must the OPTIONS HTTP method type (Well.. some of the time anyway, read on…).

This is achieved using the following Servlet filter but that’s only half the story….

Note: (The Access-Control-Max-Age value is important, keep it at 0 while developing CORS functionality. The reasons are explained at the end of this post)

There are two types of CORS request, simple and non-simple. Non-Simple is known as “Preflighted”.
A non-simple request is issued if the Content-Type of the request is any Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain.

So, Content-type = application/json for example will trigger the non-simple (Preflighted) CORS request. In addition, any custom headers in the request will also trigger the non-simple request.

Note: Access-Control-Allow-Headers should always include ‘Content-Type’.

4. Non simple requests result in two actual requests, one with HTTP method OPTIONS which will agree the contract in relation to request headers ‘Access-Control-Allow-Origin’, ‘Access-Control-Allow- Methods’ and ‘Access-Control-Allow-Headers’. This (pre) request is called a preflight request. The second request is the actual request itself.

Therefore, the server application needs to provide an end-point to accept the preflight request which will be of HTTP method type OPTIONS. The server must deliver the appropriate response headers as detailed in the Servlet filter above.

In the Ninja framework the OPTIONS endpoint is achieved with the following ‘Route’ and matching Controller method. The Filter is similar to the traditional Servlet filter above except ninja style. The filter is applied to the controller.

Note how the CORS route is a catch-all, meaning every request with HTTP method type OPTIONS is handled by this single route.

And the corresponding controller…

 

And the CORS Filter….

 

Here is the jQuery that can be used to invoke this successfully. Note the custom header in both the Filter and the ajax request Header.

 

And matching HTML to tie it all together:

 

Using firebug, the two requests can clearly be seen, preflight and the actual request:

preflight_and_actual_request

 

Note on debugging….

The response header “Access-Control-Max-Age” value is set to 0 while developing the CORS handler. This header value is the number of seconds the request should remain cached. A cached request will not initiate a Preflight request.

So, if this value is set, changing code that could result in CORS issues may not be apparent until after the cache has expired. Specify a number-of-seconds value after the CORS stuff has been ironed out. 3600 is an appropriate value. Keep in mind that there is a limit in the region of minutes which I think varies from browser to browser. Open to clarification on that point!

 

Related Posts

CORS REST Service with detached HTML 5 Client worked example with downloadable MAven project and HTML5 client http://outbottle.com/cors-stateless-rest-service-with-detached-html5-client/

Authenticating a REST Service using Facebook authentication http://outbottle.com/cors-stateless-rest-service-with-facebook-authentication/

Adding JSON Web Token (JWT) enhancements to “Authenticating a REST Service using Facebook authentication” http://outbottle.com/json-web-token-jwt-with-cors-stateless-rest-service-with-facebook-authentication/

Conclusion

This post has described Cross-Origin-Resource-Sharing (CORS) and how to achieve it.

  • The server response must specify “Access-Control-Allow-Origin” =  “*” to allow request from a different domain (where * can be a specific domain).
  • It must also permit the OPTIONS HTTP method type along with the actual request type I.e. “Access-Control-Allow-Methods” = “POST, GET, OPTIONS, DELETE, PUT”
  • The server must always permit Request header ‘Content-type’. It must also permit any other headers supplied in the client request. E.g. “Access-Control-Allow-Headers” = “Content-type, X-Foo-for-demo-only”
  • The server application must supply an end-point for the Preflight requests of HTTP method type OPTIONS. This example has utilized an excellent feature of the Ninja Framework to supply a generic OPTIONS preflight end-point.

Comments and questions are always welcome…..