HTML5 and Cross-Origin Resource Sharing call

Services are becoming more important with richer HTML5 style applications. But by default the browser will let you call into a service on the originating domain but not another one. And that means you have to include all your services one the same domain, not very practical. And worse if you want to use a 3rd part service you can’t call this directly from the your JavaScript code.

A technique that has been used for quite some time to solve this problem is JSONP. Basically when you are doing a JSONP style call the request is done by adding a DOM element to the page instead of using the XMLHttpRequest object. While JSONP works it has a number of drawbacks in that you can only do a HTTP GET and if there is an error you will not get any feedback, instead you simply never get a response.

The reason XMLHttpRequest doesn’t work by default is that the browser blocks the result as a security feature. If we open up Fiddler we actually see the request and response as expected, it’s the browser that never passes it back the calling JavaScript code.

The new HTML 5 Cross-Origin Resource Sharing, aka as CORS,  fixes this problem by allowing our code to call into services on other domains with full security still being enforced by the browser.

 

To demo CORS I am using two real simple ASP.NET MVC applications. One of these contains a simple JSON action method and both sites call this same controller action method to retrieve the JSON and display it.

   1: public ActionResult Data()

   2: {

   3:     return Json(new

   4:     {

   5:         firstName = "Maurice",

   6:         lastName = "de Beijer",

   7:         now = DateTime.Now.ToLongTimeString()

   8:     }, JsonRequestBehavior.AllowGet);

   9: }

The ASP.NET MVC view used for both sites is almost identical. There is no difference in the JavaScript, the only difference is in the header so we can distinguish between the two sites.

   1: @{

   2:     ViewBag.Title = "Cross-Origin Resource Sharing";

   3: }

   4: <h2>

   5:     Cross-Origin Resource Sharing call</h2>

   6: <button id='btn'>

   7:     Load data</button>

   8: <pre id='data'></pre>

   9: <script>
   1:  

   2:     $(function () {

   3:         $('#btn').click(function () {

   4:             $.ajax('http://localhost:6688/home/data').then(function (data) {

   5:                 $('#data').text(JSON.stringify(data));

   6:             }, function (e) {

   7:                 $('#data').text(JSON.stringify(e));

   8:             }); ;

   9:         });

  10:     });

</script>

If I open both sites in the browser and try to run the code I get these results. The image on the left is the page on the same side as the service and the image on the right shows the results of the cross domain call.

image

image


Its clear that the cross domain request didn’t work as it came back with an error status

{"readyState":0,"responseText":"","status":0,"statusText":"error"}

In order for this to work there is nothing we have to do to the JavaScript or HTML. Instead we have make a change to the HTTP headers of the response returning the JSON data. The minimum we need to do is add the Access-Control-Allow-Origin header to let the browser know HTML pages originating from which site can request this data. If you want to allow everyone access you can simply specify “*”, alternatively you can specify a specific domain.

So with a minimal change to the service this works just fine.

   1: public ActionResult Data()

   2: {

   3:     Response.AddHeader("Access-Control-Allow-Origin", "*");

   4:     return Json(new

   5:     {

   6:         firstName = "Maurice",

   7:         lastName = "de Beijer",

   8:         now = DateTime.Now.ToLongTimeString()

   9:     }, JsonRequestBehavior.AllowGet);

  10: }

image

You can get far more control though. There are a number of other HTTP headers, both on the request and response, you can use to fine tune the behavior of CORS. More complex scenarios actually result in multiple calls being made behind the scenes, first a preflight request is made to check for the required permissions and only when that is satisfied the actual request is made.

 

All together CORS is a real nice feature to have. As usual the only problem is that Internet Explorer, even IE 10, doesn’t support it Sad smile Check CanIUse.com for support details.

Enjoy!

 

[f1]

[f2]

Leave a Reply

Your email address will not be published. Required fields are marked *