Using CORS in ASP.NET WebAPI Without Being a Rocket Scientist

If you have done any extensive work with AJAX calls, then the odds are that you have fun into an unfriendly mechanism that will have your request ground to a halt known as CORS (Cross Origin Resource Sharing). Most commonly this will occur when you are attempting to pull data from a source other than where the request originated from (which raises flags in the security department) although the requests may often be legitimate.

This article will discuss the steps necessary to implement and enhance CORS support within ASP.NET Web API allowing you to handle CORS Requests at a Global, Controller or Action level to provide more flexibility when working with possible cross-origin requests.

A Course on CORS

Cross Origin Resource Sharing (CORS) as previously mentioned is a security mechanism that allows web pages, sites and applications to make AJAX calls and HttpRequests to other domains.

These cross-domain requests are forbidden within browsers that lack CORS due to famous same-origin policy for web requests (which basically states that scripts should only be permitted to run from the domain they originated from) and could lead to nasty things like XSS attacks that translate into you having a bad day.

An example CORS request can easily be created by using something like a very basic AJAX call to attempt to access today’s Google doodle :

<script type='text/javascript'>
     $(function(){
         // Create a simple AJAX Request to Google and alert the results
         $.ajax({ url: "http://www.google.com", success: function(data){
             //Spoiler Alert: This will not work.
             alert(data); 
         }});
     });
</script>

and if you check out the console area of your browser – you’ll see something that looks a bit like this :

Attempt to Access Google

Too Legit To Quit

CORS requests can actually have legitimate purposes aside from scraping screens and data from other domains too. Consider the following situation where you have two sites being hosted :

As you can see above groundcontrol.com features a Web API that you may want to attempt to consume services from using majortom.com. However, due to the way that CORS works it will recognize these two sites as coming from different origins as they have different hosts (ports and protocols can also constitute different origins) and it will block these requests even though you own both of these domains for security reasons (it doesn’t really care that you own them).

Fear not. Not all requests of this type are illegitimate and CORS features the necessary tools and protocols for handling legit cross-domain and cross-origin requests.

Ground Control to Major Tom

To demonstrate this functionality, we can create a very basic harness to make an AJAX Request that would be considered cross-domain between these two applications. Firstly, we will go into the Web API project "Ground Control" and create a very simple action to send out to our soon to be lonely astronaut.

Within the ValuesController of the Web API Project, create the following action :

public class ValuesController : ApiController
{
        public string Get()
        {
             return "Can you hear me Major Tom?";
        }
}

In order to test that this is working properly, we will also create a view that will perform an AJAX call to this project and retrieve our message :

<head runat="server">
   <meta name="viewport" content="width=device-width" />
   <title>Ground Control</title>
   <!-- jQuery Reference to handle AJAX call -->
   <script src="@Url.Content("~/Scripts/jquery-1.8.2.js")"></script>
   <!-- Our script to actually fire the call itself -->
   <script type="text/javascript">
       // When the page loads
       $(function () {
          // Fire this AJAX call
          $.ajax({
             url: "http://localhost:1337/api/values",
             type: "GET",
             success: function (data) {
                // Grab our data from Ground Control
                alert(data);
             },
             error: function (event) {
                // If any errors occurred - detail them here
                alert("Transmission Failed. (An error has occurred)");
             }
         });
      });
  </script>
</head>

Notice that we have explicitly defined this as running on port 1337, which can easily be set through the Properties of your Application :

Server Settings

and then when we navigate to the Home Controller's Index View, we will be presented with our AJAX Request from the Web API call.

Stepping through the Door

Now it doesn't do much good to have our Ground Control communicating with themselves, so let's try sending these transmissions to our acclaimed astronaut Major Tom who is deep in the depths of space where there are no doubt all types of CORS-related interference to mess with our transmission.

To do this, we will create a very basic MVC Controller within the same solution to function as our Major Tom. Within this Project, we will take the following steps :

  1. Create a HomeController.
  2. Create a View for the Index Action of the Home Control.
  3. Copy the exact contents of the Index View from the Web API Project into the corresponding MVC View.

After performing these steps, we are going to attempt to run our MVC Project to see if we can make contact with Ground Control :

Transmission Failed

Although this application was targeting the proper URL and port of our WebAPI project, the transmission did not go through successfully. Using the Developer Tools (F12) within your favorite browser, you can check out the Console to see exactly what the cause of the error was :

An example of a CORS request gone wrong.

As you might have expected, CORS determined that the request that was being made within the AJAX call was a cross-browser / cross-domain request and it quickly denied it. So it appears that Ground Control will actually need to determine a method to get this message out to Major Tom in time.

Technical Difficulties with Ground Control

This issue can be resolved by making the following change within the web.config file of our Web API :

<system.webServer>
    <httpProtocol>
        <customHeaders>
            <!-- Adding the following custom HttpHeader 
                 will help prevent CORS from stopping the Request-->
            <add name="Access-Control-Allow-Origin" value="*" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Adding the Access-Control-Allow-Origin header with a value set to an asterisk (e.g. wildcard) will allow any origin to make requests to the Web API. So after making these changes within our Web API Project, we can again try running the MVC Application to see if it can now properly receive the transmission being sent from Ground Control :

No longer sitting in a tin can.

So with the minor addition of the Access-Control-Allow-Origin header, we can make successfully make CORS requests in between different applications using Web API. If you want to test this out or expand upon the functionality in this example, you can download it below :