Une traduction française est disponible ici.
Cross-Domain AJAX request is the developer’s nightmare with the awful JSONP workaround. But we can use a simple standard to kick off this bad practice.
For a recent project I refused to use JSONP to make my REST API cross-domain compatible, so I looked for an alternative solution. This solution is Cross-Origin Resource Sharing (CORS), a W3C standard.
In the last revision of the document, new headers are added to the HTTP protocol (Not used by RFC 2616) and a special request (preflight request) was created for cross-domain rights access control during an AJAX request.
- Origin: shows the request domain
- Access-Control-Request-Method: shows the request HTTP verb
- Access-Control-Request-Headers: shows additional headers used by browser and must be authorised by server to continue AJAX requests
- Access-Control-Allow-Origin: indicates authorised domains to make cross-domain requests (should contain at least value of ‘Origin’ header or ‘*’)
- Access-Control-Allow-Credentials: indicates if server allow credentials during CORS requests
- Access-Control-Expose-Headers: indicates allowed headers to be sent to the browser
- Access-Control-Max-Age: indicates how long a response to a preflight request can be cached
- Access-Control-Allow-Methods: indicates all allowed HTTP verbs for cross-domain requests (should contain at least the ‘Access-Control-Request-Method’ header value)
- Access-Control-Allow-Headers: indicates allowed custom headers to be used by browser during cross-domain requests (should contain at least the ‘Access-Control-Request-Headers’ header value)
In this post, I don’t use Access-Control-Allow-Credentials, Access-Control-Expose-Headers and Access-Control-Max-Age headers.
How does it work ?
For standard requests, the browser will add Origin and Access-Control-Request-Method headers. A preflight request will be executed before the actual request if it contains custom headers, if it uses another HTTP verb than GET or POST or also if the body isn’t in text/plain format (ie. application/json).
There is a preflight request made by Firefox :
OPTIONS /url HTTP/1.1 Host: 127.0.0.1:5555 User-Agent: Mozilla/5.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Origin: http://127.0.0.1 Access-Control-Request-Method: POST Access-Control-Request-Headers: x-requested-with
We can see Origin, Access-Control-Request-Method and Access-Control-Request-Headers headers. After this request, Firefox waits a similar response:
X-Powered-By: Servlet/3.0 Server: GlassFish Server Open Source Edition 3.0.1 Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: x-requested-with
After this, Firefox can continue with its requests and adds a custom header:
And our API ?
Well, now we modify our API to be CORS-compliant using Java and Jersey. You can add a simple method like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Because of I didn’t find the catch-all @Path, we need to add methods as many as paths the API manage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
These code snippets are given only as an example, you can change it to build Access-Control-Allow-Methods according to the API’s WADL scheme or add a restrictive Access-Control-Allow-Origin rule.
What about browser compatibility ?
With this standard you can miss Internet Explorer 6 and 7. Internet Explorer 8 is saved by a new XDomainRequest object replacing XMLHttpRequest but seems to be not compatible with preflight requests. Other browsers are globally compatible with their last versions.
- W3C Worksheet about CORS
- Using CORS with Firefox 3.5
- XDomainRequest object on MSDN
- RFC 2616: HTTP Protocol
UPDATE: 2012/12/23 - fixing typo on line 4,
ResponseBuilder has no
ok() method (Thanks Frankie Frank)