Enabling CORS in ASP.NET Web API

A Guide of How to Enable CORS in ASP.NET Web API

sonia jessica
Aug 11 · 10 min read

Introduction:-

ASP.NET is a framework for developing web applications that expands the .NET platform with tools and libraries. WebAPI for ASP.NET is a popular technology.

Everyone is attempting to use AJAX requests or the server side to access the service. Your web browser is unable to make AJAX requests to a server in another domain due to security limitations in your browser’s security policy. This is often referred to as the “same-origin scheme.”

To put it another way, built-in browser protection prevents one domain’s web page from making AJAX calls to another domain. The issue arises when a WebAPI is hosted and another application, from a different domain, attempts to access it through an AJAX request. Enabling Cross-Origin Resource Sharing (CORS) in WebAPI is crucial in this case.

CORS is a W3C standard that allows you to bypass the browser’s same-origin policy, which limits access from one domain to resources belonging to another. Using the appropriate Web API package, you can enable CORS for your Web API.

A scheme, a host, and a port number make up the origin of a request. If two requests have the same scheme, host, and port number, they are assumed to be from the same origin. If all of these are different, the requests are called cross-origin, meaning they don’t come from the same source.

A web API can assist you in creating an AJAX-based ASP.NET programme. A web API framework makes it simple to build services that can operate on a variety of entities. As a result, web API makes it easier for developers to create an ASP.NET application that works with almost any browser and computer. Web APIs give you access to all of HTTP’s features, such as URIs, request/response headers, content formatting, caching, and so on.

As a result, it’s much simpler to build ASP.NET web applications using RESTful web services via Web APIs than it is to do so with WCF (Windows Communication Foundation) rest services which require specifying extra configuration settings for different devices.

Ways to enable CORS in Web API:-

Using JSONP:-

JSONP is an abbreviation for JSON with Padding. It aids in implementing cross-domain requests by browser’s same-origin policy. It encapsulates a JSON response in a JavaScript function, that is, callback function) and sends it back to the browser as a Script. This allows you to load JSON from an external server into the JavaScript on your webpage, bypassing the same-origin policy.
For example:-
Let us suppose we have the following JSON:-

//JSON
{
‘rollNo’ : ‘1’,
‘name’ : ‘Vaibhav’,
‘Maths’ : ‘100’,
‘Physics’ : ‘66’,
‘Chemistry’ : ‘97’,
‘Biology’ : ‘88’
}

When the server receives the “callback” parameter in JSONP, it wraps the result in a different way and returns like this:-

//JSONP
newStudent({
‘rollNo’ : ‘1’,
‘name’ : ‘Vaibhav’,
‘Maths’ : ‘100’,
‘Physics’ : ‘66’,
‘Chemistry’ : ‘97’,
‘Biology’ : ‘88’
});

We must first allow CORS in WebAPI, and then call the service using an AJAX request from another program. To allow CORS, we’ll need to download and install the JSONP package from NuGet. We need to install the package WebApiContrib.Formatting.Jsonp which provides a JSONP MediaTypeFormatter implementation for ASP.NET Web API.

After installing the Jsonp package, add the following code to the App_StartWebApiConfig.cs file:-

var FormatterJSONP = new JsonpMediaTypeFormatter(config.Formatters.JsonFormatter); 
config.Formatters.Add(FormatterJSONP);

It makes a JsonpMediaTypeFormatter instance and adds it to the config formatters object.
Now that CORS has been activated in Server, the other application must send AJAX requests to a domain that is not ours. In the code snippet below, the datatype is set to jsonp, which is compatible with cross-domain requests.

<script src=”http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script> 
<script>
$(document).ready(function () {
$.ajax({
type: ‘POST’,
url: ‘http://localhost:3000/api/Recipes’,
cache: ‘true’,
dataType: ‘jsonp’,
success: function (json) {
var pageContent = “”;
$.each(json, function (key, item) {
pageContent = pageContent + “<tr><td>” + item.Number + “</td><td>” + item.Name + “</td><td>” + item.Recipe + “</td></tr>”;
});
$(‘#Recipes’).append(pageContent);
},
error: function (parsedJSON, Status, thrownError) {

$(‘body’).append(
“Status of parsedJson : “ + parsedJSON.status + ‘</br>’ +
“errorStatus: “ + Status + ‘</br>’ +
“thrownError: “ + thrownError);

}
});
});
</script>

Disadvantages of using JSONP:-

  • There are a lot of security issues involved which include our cookies being stolen. This is a matter of great concern.
  • Old browsers do not support JSONP. Hence it is not compatible with all browsers.

Using Microsoft.AspNet.WebApi.Cors:-

First of all, we need to install Microsoft.AspNet.WebApi.Cors package from NuGet package. For this, go to Tools Menu => Library Package Manager => Package Manager Console and run the following command:-
Install-Package Microsoft.AspNet.WebApi.Cors
After this, we will use the EnableCorsAttribute class to register/enable CORS, and it has four parameters out of which the last one is optional. The four parameters are as follows:-

  • Origins:-
    Here, we must specify the origins or the domain from which requests will be accepted. If you have several domains, you can make them comma-separated. Furthermore, if you want any domain request to be approved, use “*” as a wildcard.
  • Request Headers:-
    Which Request headers are enabled is specified by the Request header parameter. Set the value to “*” to enable any header.
  • HTTP Methods:-
    The methods parameter specifies the HTTP methods that can access the resource. When using multiple HTTP methods such as “get, put, post,” use comma-separated values. Use the wildcard value “*” to enable all HTTP methods.
  • exposedHeaders:-
    The browser does not display all of the response headers to the application by default. Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma are the response headers that are accessible by default.

You must use exposedHeaders to render other headers visible in the browser. You can build custom headers using the code snippet below:

[EnableCors(origins: “*”, headers: “*”, methods: “*”, exposedHeaders: “SampleHeader”)] 
public class SampleController : ApiController
{
}

CORS Support for Web API is configurable at three levels:-

Global level:-
We will allow CORS at the global level, which means it will apply to all controllers and their actions. In the App Start/WebApiConfig.cs file, add the following code snippet. It generates an instance of the EnableCorsAttribute class with the following parameters passed in:-
http://localhost:3000/SampleApp/Form1.aspx"
For this domain, the server has enabled CORS.

public static void Register(HttpConfiguration config) 
{
EnableCorsAttribute cors = new EnableCorsAttribute(“http://localhost:3000/SampleApp/Form1.aspx", “*”, “GET,POST”);
config.EnableCors(cors);
}

STAR indicates that it supports all request headers.
“GET, POST”: This means that it only acknowledges GET and POST http verbs. If the server receives some request other than “GET, POST,” it throws an exception.

Controller level:-
We can allow CORS at the controller level, which means that all of the actions within are ready to serve cross-domain requests. Add the EnableCors attribute to the top of the controller and transfer the appropriate parameter (same as discussed above).


[EnableCors(origins: “http://localhost:3000/SampleApp/Form1.aspx", headers: “*”, methods: “*”)]
public class SampleController : ApiController
{

}

Action level:-
Similar to the controller level, we can allow CORS at the Action level, which means that CORS is activated for a specific action that is ready to serve cross-domain requests. We must add the EnableCors attribute to the top of the action and transfer the appropriate parameter (same discussed above).

public class SampleController : ApiController 
{
[EnableCors(origins: “http://localhost:3000/SampleApp/Form1.aspx", headers: “*”, methods: “*”)]

public IEnumerable<string> Get()
{
return new string[] { “string1”, “string2” };
}
}

Enabling CORS in Web API 1.0:-
If you are using WebAPI 1.0, you’ll need to modify the Global.asax file to include the following code. In this case, we used the Application BeginRequest() event to allow CORS, which checks the origin name and then adds headers to the response object.

protected void Application_BeginRequest() 
{
string[] origin_Allowed = new string[] { “http://localhost:3001", “http://localhost:3013" };
var origin = HttpContext.Current.Request.Headers[“Origin”];
if (origin != null && origin_Allowed.Contains(origin))
{
HttpContext.Current.Response.AddHeader(“Access-Control-Allow-Origin”, origin);
HttpContext.Current.Response.AddHeader(“Access-Control-Allow-Methods”, “GET,POST”);
}
}

Enabling CORS from the Web.Config file of the project:-

<system.webServer>
<handlers>
<remove name=”ExtensionlessUrlHandler-Integrated-4.0" />
<remove name=”OPTIONSVerbHandler” />
<remove name=”TRACEVerbHandler” />
<add name=”ExtensionlessUrlHandler-Integrated-4.0" path=”*.” verb=”*” type=”System.Web.Handlers.TransferRequestHandler” preCondition=”integratedMode,runtimeVersionv4.0" />
</handlers>
//Add this part of code in the file
<httpProtocol>
<customHeaders>
<add name=”Access-Control-Allow-Origin” value=”*” />
<add name=”Access-Control-Allow-Credentials” value=”true”/>
<add name=”Access-Control-Allow-Headers” value=”Content-Type” />
<add name=”Access-Control-Allow-Methods” value=”GET, POST, PUT, DELETE, OPTIONS” />
</customHeaders>
</httpProtocol>
<! — End of new addition →
</system.webServer>

This enables CORS for all controllers in your project at once.

Passing credentials in Cross-Origin requests:-

In a CORS request, credentials must be handled differently. When a cross-origin request is made, the browser does not submit any credentials by default. Cookies and HTTP authentication systems are examples of credentials. The client must set XMLHttpRequest.withCredentials to true to submit credentials with a cross-origin request.
Using XMLHttpRequest directly:

//C#
var temp = new XMLHttpRequest();
temp.open(‘get’, ‘http://localhost:3001/api/sample');
temp.withCredentials = true;
//jQUERY
$.ajax({
type: ‘get’,
url: ‘http://localhost:3001/api/sample',
xhrFields: {
withCredentials: true
}

In addition, the credentials must be enabled by the server. Set the SupportsCredentials property on the [EnableCors] attribute to true to allow cross-origin credentials in Web API:

//C#
[EnableCors(origins: “http://localhost:3001/api/sample", headers: “*”,
methods: “*”, SupportsCredentials = true)]

If this property is valid, an Access-Control-Allow-Credentials header will be included in the HTTP response. This header informs the browser that the server accepts cross-origin credentials. The browser will not expose the response to the application if the response does not contain a valid Access-Control-Allow-Credentials header, and the AJAX request will fail.
Setting SupportsCredentials to true should be avoided because it means that a website on a different domain will send a logged-in user’s credentials to your Web API on their behalf without the user’s knowledge. Setting origins to “*” is also invalid if SupportsCredentials is true, according to the CORS specification.

Custom CORS Policy Provider:-
The ICorsPolicyProvider interface is implemented by the [EnableCors] tag. Create a class that derives from Attribute and implements ICorsPolicyProvider to have your own implementation.
For Example:-
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]

//C#
public class CustomCORSPolicy : Attribute, ICorsPolicyProvider
{
private CorsPolicy myPolicy;public CustomCORSPolicy()
{
// Creating a CORS policy.
myPolicy = new CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true
};
// Adding allowed origins.
myPolicy.Origins.Add(“http://localhost:3001/");
myPolicy.Origins.Add(“http://localhost:3013/");
}
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request)
{
return Task.FromResult(myPolicy);
}
}

You can now use the attribute wherever you would put [EnableCors].

[CustomCORSPolicy]
public class SampleController : ApiController
{

You can register an ICorsPolicyProviderFactory object that creates ICorsPolicyProvider objects instead of using attributes.

//C#
public class CustomCorsPolicyFactory : ICorsPolicyProviderFactory
{
ICorsPolicyProvider providerCORS = new MyCorsPolicyProvider();
public ICorsPolicyProvider GETCorsPolicyProvider(HttpRequestMessage request)
{
return providerCORS;
}
}

Call the SetCorsPolicyProviderFactory extension method at startup to set the ICorsPolicyProviderFactory:

public static class WebApiConfig
{
public static void Register(HttpConfiguration configuration)
{
configuration.SetCorsPolicyProviderFactory(new CustomCorsPolicyFactory());
configuration.EnableCors();
// …
}
}

Preflight Request:-

Certain types of requests, such as DELETE or PUT, must go a step further and seek permission from the server before proceeding. A preflight request is used by the browser to request permissions.

The browser sends a tiny request called a preflight request before the actual request. It includes details such as the HTTP method used and whether any custom HTTP headers are present. The preflight allows the server to see how the actual request would appear before it is sent. The server will then tell the browser whether or not to submit the request, or whether to return an error to the client instead.

The preflight concept was created to make cross-origin requests without breaking existing servers that rely on the browser’s same-origin policy. If the preflight reaches a CORS-enabled server, the server will recognize the request and react appropriately.

However, if the preflight reaches a server that is unaware of or unconcerned about CORS, the server will not submit the appropriate preflight response, and the actual request will never be submitted. Unsuspecting servers are protected from processing cross-origin requests they don’t like.

A preflight request has three characteristics: it uses the HTTP OPTIONS method, it includes an Origin request header, and it includes an Access-Control-Request-Method header.

Preflight Request errors:-

Even when all is configured correctly in Web API, when making a preflight request from a script, the preflight request returns an HTTP Error Code 405 more often than not. This can be resolved by making a few changes to the web.config and Global.asax files.

//Web.config file
<system.webServer>
<handlers>
<remove name=”ExtensionlessUrlHandler-Integrated-4.0" />
<remove name=”OPTIONSVerbHandler” />
<remove name=”TRACEVerbHandler” />
<add name=”ExtensionlessUrlHandler-Integrated-4.0" path=”*.” verb=”*” type=”System.Web.Handlers.TransferRequestHandler” preCondition=”integratedMode,runtimeVersionv4.0" />
<! — Add this line of code →
<add name=”OPTIONSVerbHandler” path=”*” verb=”OPTIONS” modules=”ProtocolSupportModule” requireAccess=”None” responseBufferLimit=”4194304" />
<! — End of new addition →
</handlers>
<httpProtocol>
<customHeaders>
<add name=”Access-Control-Allow-Origin” value=”*” />
<add name=”Access-Control-Allow-Credentials” value=”true”/>
<add name=”Access-Control-Allow-Headers” value=”Content-Type” />
<add name=”Access-Control-Allow-Methods” value=”GET, POST, PUT, DELETE, OPTIONS” />
</customHeaders>
</httpProtocol>
</system.webServer>
// Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
<! — Added code →
protected void Application_BeginRequest(object Sender, EventArgs eventE)
{
if (HttpContext.Current.Request.HttpMethod == “OPTIONS”)
{
HttpContext.Current.Response.Flush();
}
}
<! — End of added code →
}

Disabling CORS:-

If CORS is enabled at the global or controller level, then CORS is enabled for all activities. However, if you want to disable CORS for a few acts for security reasons, the DisableCors attribute comes in handy. It disables CORS, which means other domains won’t be able to call the action.

[DisableCors()] 
public string Get(int id)
{
return “string”;
}

CORS is a server-side application that operates in conjunction with the browser. As a result, browser support for CORS is also needed.

Conclusion:-

Web API is an extensible platform for ASP.NET development that serves us information from the server. It is entirely built on the HTTP protocol and is simple to describe, expose, and consume in a RESTful manner.

On the.NET framework, it is regarded as an ideal forum for creating RESTful applications. CORS (Cross-Origin Resource Sharing) is a protocol that allows users to make requests from one website to another in a browser that are usually prohibited by another regulation known as SOP (Same Origin Policy).

It allows clients or browsers to send secure cross-origin requests and data to servers. Requests from various backgrounds are referred to as cross-origin requests. For JavaScript, CORS simply resolves the same-origin constraint. CORS for web APIs can be allowed using the appropriate web API kit or OWIN middleware.

In this article, we saw different ways of enabling CORS. We addressed two methods for allowing CORS: JSONP and the Microsoft Cors kit. Microsoft Cors’ browser incompatibility triumphs over JSONP for protection purposes.

Easyread

Easy read, easy understanding.