Cross Origin Resource Sharing (CORS) in ASP.Net Web API 2

In my previous article, we discussed the concept of Route and RoutePrefix that was introduced as a new feature in WebApi2. Continuing on the same lines, we will now discuss the concept of Cross Origin Resource Sharing or C.O.R.S.

CORS is a concept about permissions to make cross-domain ajax calls. Before we move further, let's understand the concept of cross-domain.

Two domains can be called as different domains:

  1. When they differ in schemes (in other words http or https).
  2. When they have different port numbers.
  3. When they have different domains or sub-domains.

In normal scenarios, an ajax call cannot be made to a resource on another domain, until the content-type is set to jsonp. The same is the case when we try to make an ajax call to a webapi in a different domain. When you try to make a call to a web API hosted on another domain, then you will get the following error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:34290/GetString?_=1420815804319. This can be fixed by moving the resource to the same domain or enabling CORS.

To enable a cross-domain request in the webapi, we need to add the reference to the Microsoft ASP.Net Web API 2.2 Cross-Origin Support libraries, apart from the basic webapi libraries.

Web API 2

Next, to test this implementation, we will create a new WebApiController (in an MVC application) and a client application with an HTML page, that will send requests to this webapi. To start with, we will first try to see the issue in making cross-domain ajax calls in the webapi. Then we will enable the CORS feature and see the results. So let's start by creating a webapi controller.

Web API

Next, we remove all the methods in the default API controller and add a simple method GetString, that returns a string.

default api

Run the MVC application. This will result in the hosting of the webapi on the same local development port, as the normal MVC application. Now, we add a client application and make an ajax request to the webapi URL. So our code becomes:

code

Run the client application and also open the browser console and see the message we receive.

message
This is the message that we discussed above in the start. An important point to note here is that your request will hit the web API. If you add a break-point on the webapi method it will be hit. But the response is blocked by the browser, as per the CORS policy.

To enable the cross-domain request, we add the following code in the Register method of the WebAPIConfig.cs file.

config.EnableCors(new EnableCorsAttribute("*", "*", "GET"));

Let's first test the request and see the results. Then we will discuss what this function does exactly. So send the client request again and see the results.

client request again

So now we can see the results. Remember, the request gets sent and hits the API even without enabling this feature, but the issue is, we cannot see the results until this feature is enabled. So let's discuss the statement that enabled this feature.

To start with the, config.EnableCors enables the API to receive the requests from other domains. We can do this either at the controller action level, at the entire controller level or globally on all the controllers.

EnableCors() overload with no parameter

When we pass no parameter to this function, then we must add the [EnableCors] attribute explicitily, on the API action, on which we would like to allow the cross-domain requests. Simply adding the config.EnableCors. Doing it at the WebApiConfig.cs level will not work.

We can also set this attribute on the controller level. In such a case, we expose all the methods of the controller to the ajax requests from outside its domains. So what if you need to disable this for one of the controller methods. In that case, add a [DisableCors] attribute on that method.

When we enable this feature using the [EnableCors] at the controller or action level, we also need to set certain parameters, that include:

  1. origins: Specifies the URL from which we want to get cross-domain requests. We can add multiple values in a comma-separated format.

  2. headers: Specifies that author request headers are allowed.

  3. methods: Specifies the type of request, in other words GET, POST, PUT and so on, that should be enabled on this method.
See the code below, we enable the CORS feature in WebApiConfig.cs with no parameter and add the [EnableCors] parameter at the action level.

action level

EnableCors() overload with parameter

This is the version of the function that we used earlier in the start of the discussion. This will enable the cross-domain access at the global level, in other words on all the API controllers, until we apply the [DisableCors] attribute explicitly on the controller actions. So let's add these settings at the global level.

cs code

Here, the use of "*" means ALL, in other words all origin types are allowed to make requests to this API, all the header types are allowed and GET request types from another domain are allowed.

Let's test the code with the [DisableCors] attribute and a global configuration to enable cross-domain requests. So our code becomes:

DisableCors

Since we have disabled the request for the method named GetSecondString(), it again results in an error related to the cross-domain policy for this method. However, the call to the method GetFirstString() works as expected. See the results for the request to the second method:

call faild

And, the request for the first method works fine.

request for the first method
One very interesting issue you might encounter is that when you omit the {action} parameter in the routing template, it will cause issues since there are two or methods with the same signature and that have the same type, in other words GET, POST and so on. For example, when you have two GET methods named FindProduct and GetProduct, both taking a single input parameter of type int, then you will get the issue:

"Multiple actions were found that match the request: FindProduct on type WebAPI.Controllers.ProductsControllerGetProduct on type ProductsController"

The reason is that when the action name is not specified, it will have two methods of the same type GET mapping to the same type of parameters and it will not be able to identify which method is to be invoked. Change the signature of one of the methods and it will work.

So this was about the feature of cross-origin resource sharing in the WebAPI.