ASP.NET Web API v2 Cross-origin resource sharing made easy

Building web apps that rely on several data endpoints located at different locations are a common practice these days. By default, due to security concerns like XSS attacks, CORS requests are blocked. That means that if your JavaScript code tries to make an Ajax call to a different location, other then the domain it originated from, it will fail with HTTP error code 405 (Method not allowed).

Let’s suppose you have an ASP.NET app that is relying on Ajax calls to fetch data from server and uses some sort of JavaScript templating for rendering and displaying data as HTML. At some point in time you’ll feel the need to expose data not only for your web app but also for a partner app or maybe for a JavaScript widget that can reside on a multitude of websites. In order to have a single data endpoint to handle all these requests a wise decision is to decouple this function from your original web app into a separate project that uses ASP.NET Web API engine. Calling the API from different .js modules and locations will be possible only if you enable CORS on the server-side and the browser that makes the calls can handle cross-origin request, luckily all modern browsers have CORS support.

With ASP.NET Web API you can enable CORS requests at application level or even target a specific controller or action. If you want to enable CORS globally, the easiest way is by adding the following lines to your web.config file:

<system.webserver>
    <httpprotocol>
      <customheaders>
        <add name="Access-Control-Allow-Origin" value="*"></add>
      </customheaders>
    </httpprotocol>
  </system.webserver>

If you need a better control on controllers and actions that should support CORS, then you’ll have to install Microsoft.AspNet.WebApi.Cors via NuGet and use the EnableCors and DisableCors attributes. After installing Web API CORS package add this line to the Register method inside WebApiConfig.cs file:

using System.Web.Http.Cors;

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
	//CORS enabled
        config.EnableCors();
    }
}

Targeting a specific controller or action can be accomplish like this:

[EnableCors(origins: "*", headers: "*", methods: "*")]
public class ItemsController : ApiController
{
    public HttpResponseMessage Get() { ... }
    public HttpResponseMessage Get(int id) { ... }

    [DisableCors]
    public HttpResponseMessage Post() { ... }
}

If you want to restrict access to a domain white-list you can supply the list to the Origin parameter, white-listing is supported for Headers and Methods also:

[EnableCors(
	origins: "http://localhost,http://my-app.com", 
	headers: "accept,content-type,origin", 
	methods: "get,post")]
public class ItemsController : ApiController
{
	public HttpResponseMessage Get() { ... }
	public HttpResponseMessage Post() { ... }
	public HttpResponseMessage Put() { ... }
}

If your business model requires that access is granted based on supplied credentials you can specify that too, in this case you should use an origin white-list to be compliant with the CORS specs. Be aware that providing a domain white-list to EnableCore attribute will not block any requests to your API, what CORS does is preventing the browser from getting the result of the request.
If you want to restrict access to a list of domains, the right way to do it is throw a custom AuthorizationFilter attribute that extracts the Origin header and compares it to the white-list.

[EnableCors]
public class ItemsController : ApiController
{
    public HttpResponseMessage Get() { ... }
	
    [EnableCors(
		origins: "http://www.my-app.com", 
		headers: "*", 
		methods: "*", 
		SupportsCredentials = true)]
    public HttpResponseMessage Post() { ... }
}

When posting data from JavaScript using jQuery AJAX you can inject the required headers using beforeSend function. If your API uses basic authorization then you have to encode in base64 the username and password as described below.

$.ajax({
	type: "POST",
	url: "http://my-api.net/api/items",
	data: JSON.stringify($newItems),
	dataType: 'json',
	xhrFields: {
	  withCredentials: true
	},
	beforeSend: function (request)
	{
	  request.setRequestHeader("Content-Type", "application/json");
	  request.setRequestHeader("Authorization", 
		"Basic " + window.btoa($user + ':' + $pw));
	}
}).done(function( data ) {
    alert(data);
});

You can also define your own policy provider by implementing ICorsPolicyProvider interface, for an in-depth look at EnableCors attribute please consider reading the aspnet webstack documentation on codeplex.

One Response to ASP.NET Web API v2 Cross-origin resource sharing made easy
  1. Mark Oppenheim Reply

    In your first example I think you’ll find you need some camel case for the element names; i.e: httpProtocol and customHeaders.

Leave a Reply

Your email address will not be published. Please enter your name, email and a comment.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>