Cross-domain Ajax with CORS
In my last post, I talked about how you could achieve cross-domain Ajax using JSONP which uses the script tag rather than the XMLHttpRequest object. In this post, I will show you the emerging support for cross-domain Ajax support with XMLHttpRequest using Cross-Origin Resource Sharing (CORS). First let’s make sure you understand the problem that is being solved. With regular Ajax based on the XMLHttpRequest object there is a limitation caused by the same-origin policy which means that you can submit Ajax requests only to the domain from which your JavaScript was served. If you attempt to send a request to an external 3rd-party site using XMLHttpRequest you would get a failure.
Today there is a solution allowing cross-domain Ajax requests with XMLHttpRequest available with these browsers: (Internet Explorer 8+, Firefox 3.5+, Safari 4+, and Chrome) that many developers are unaware of. These browsers achieve this using the protocol known as Cross-Origin Resource Sharing or CORS. CORS is a W3C draft specification defining client-side cross-origin requests. CORS uses custom HTTP headers which are read and set by the client and server to determine if a request should succeed or fail.
Using the CORS specification, a client can make a request to a cross-domain server and indicate that this request is to be recognized as a cross-domain request by including the Origin header, like this:
Origin: http://www.timothyfisher.com
Upon seeing the Origin header, the server can determine whether or not it wants to service the request. If the server chooses to service the request, it would include the Access-Control-Allow-Origin header in its response with the requesting domain specifically called out, like this:
Access-Control-Allow-Origin: http://www.timothyfisher.com
Back in the browser, this header is checked to verify that the domain matches the domain specified in the Origin header. If they match the request is allowed to proceed. If they do not match or the Access-Control-Allow-Origin header is missing, the request is disallowed. If you are using one of the previously mentioned supported browsers, this all happens automatically when you use the XMLHttpRequest object to make a cross-domain request. You do not have to write any additional code to add or check these headers yourself. This mechanism is relatively simple and works for any GET or POST requests. Of course this also assumes the the server application which you are making the request of also understands and has implemented CORS support.
If you want to send a request that uses an HTTP method other than GET or POST, you must first send what is called a preflight request using the HTTP OPTIONS method. Preflight requests are supported byFirefox 3.5+, Safari 4+, and Chrome. Unfortunately, Internet Explorer 8 does not support them.
Let’s assume you wanted to use the HTTP DELETE method. This would require the preflight request, and using the XMLHttpRequest object, you could send the preflight request using code similar to this:
function deleteItem(itemId, updateUI) {
var client = new XMLHttpRequest()
client.open("DELETE", "http://calendar.example/app")
client.onload = updateUI
client.onerror = updateUI
client.onabort = updateUI
client.send("id=" + itemId)
}
Notice here the use of onload instead of onreadystatechange. This would result in additional headers being added to the request like this:
Origin: http://www.timothyfisher.com
Access-Control-Request-Method: DELETE
Upon receiving this request, the server would determine if it wants to support the method being requested from this origin. If it will accept the request, it will respond with the following headers:
Access-Control-Allow-Origin: http://www.timothyfisher.com Access-Control-Max-Age: 3628800 Access-Control-Allow-Methods: PUT, DELETE
The Access-Control-Max-Age header specifies the amount of time for which this preflight request is good for. After the specified amount of time expires, you must issue a new preflight request for additional requests. The Access-Control-Allow-Methods header specifies which HTTP methods the service will accept from the specified origin. Just as with the GET and POST requests, the browser (assuming it is one that supports the preflight requests) will handle all of the details for you. You should not have to do any additional coding yourself.
For a more complete description of using CORS with some additional use cases described including credentialed requests check out thisblog post from Nicholas Zakas. Between his post and the CORS specification, you should be able to fill in the details you need to start using CORS in supported browsers today.