BLOG

Cross Domain REST with Backbone.js and .NET MVC 3

I’m working on a small project within a bigger web application where an all-javascript solution made the most sense. In the past I’ve used Knockout.JS and had a lot of fun with it. It was simple to learn and easily added some robust javascript functionality on top of a .NET MVC project that would have been much more difficult in straight jQuery.

For this project, however, Backbone.js made more sense because we wanted all the logic to be built without having to add any server- side code. All data access would be done though a separate set of web services.

The problem is:

  1. Backbone.js wants to implement a REST interface to backend web services
  2. The web services I would be accessing would not be on the same domain as the javascript.
  3. And some combination of IIS and Windows are not friendly to REST. Specifically PUT and DELETE requests can easily get blocked.

I ended up giving up on trying to implement REST with the correct HTTP PUT AND DELETE methods. Several searches and attempts pointed possibly to WebDAV interfering. I also found other sites (like the IIS Express FAQ) that said PUT and DELETE just needed to be allowed in the applicationHost.config file. Nothing worked. But more importantly, even if I had it working in a local dev environment within Visual Studio, it didn’t look like things would be easy to deploy. Instead I took advantage of the fact that MVC 3 and Backbone.js support X-HTTP-Method-Override. Just set Backbone.emulateHTTP = true; and you can use REST-like attributes like [HttpPut] on your controller actions.

Now for the cross domain part….

When making a cross domain connection, jQuery will do the following:

  1. Send a preflight request with an OPTIONS method. This request will verify whether the server will allow the real request. The response from the server must include:
    • A list of valid HTTP methods (Access-Control-Allow-Methods)
    • A list of valid HTTP headers (Access-Control-Allow-Headers)
    • The requesting domain in the Access-Control-Allow-Origin header.
  2. If the OPTIONS request was successful, it will send the real request. This request must also respond with a valid Access-Control-Allow-Origin header.

Here is and ActionFilterAttribute I use to accomplish that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AllowCrossSiteJson : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        HttpContext.Current.Response.Cache.SetNoStore();
 
        var headers = Enumerable.ToList(HttpContext.Current.Request.Headers.AllKeys);
        headers.Add("X-HTTP-Method-Override");
 
        filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
        filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Headers", string.Join(", ", headers));
 
        filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", "*");
 
        base.OnActionExecuting(filterContext);
    }
}

The problem, however, is that even with that in place, you won’t necessarily have anything setup to respond to a request with an HTTP OPTIONS method. In my case, I setup a route and controller action to specifically handle that. It doesn’t do anything other than return the correct headers from the ActionFilterAttribute. There’s probably a better way to handle this, but it’s working nicely for me right now.

And finally, change the jQuery setting to turn CORS on:

$.support.cors = true;
calendartwitterfeedenvelopelinkedingithub-altbitbucket