What's a cross-origin http request?
This is a request made by a resource to a resource in a different domain, protocol, port to its own. For example, an HTML page at "http://domain1.com" makes a request to a resource at "http://domain2.com".
Why are we talking about this?
Well, this is sort of a 'big deal' because for security purposes, browsers restrict cross-origin requests intitiated from within scripts. So web apps can only make requests within it's domain. Kind of limits the apps, don't you think? This is where CORS comes in.
What is CORS?
CORS stands for Cross-Origin Resource Sharing. It is a standard that works by adding HTTP headers that allow servers to describe the set of origins that are permitted to read information using a web browser and the kind of request that are allowed.
Also, for HTTP request methods that can cause side-effects on server's data (usually HTTP requests other than GET), the specification mandates that browsers "preflight" the request i.e asking the server what methods are supported. It does this with an HTTP OPTIONS request method, and then, upon "approval" from the server, it send the actual HTTP request.
CORS in ASP.NET WEB API 2
In this article, I'll be showing how to enable CORS on an ASP.NET WEB API server.
For this article, I assume you already have a Web API project created.
Enabling CORS can be done in a couple of ways, which are as follows:
Enabling CORS per controller.
First, install the CORS package. In the Package Manager Console, type the following command: Install-Package Microsoft.AspNet.WebApi.Cors
.
Navigate to the App_Start folder and open the WebApiConfig.cs file. Modify the WebApiConfig.Register to reflect the following changes:
using System.Web.Http;
namespace CORSWithWebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Modification
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
Then, add the [EnableCors] attribute to the Controller class specifying origin, headers and methods. If you set [EnableCors] on the controller class, it applies to all the actions on the controller. To disable CORS for an action, add the [DisableCors] attribute to the action.
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
namespace CORSWithWebAPI.Controllers
{
[EnableCors(origins: "http://client.domain", headers: "*", methods: "*")]
public class SampleController : ApiController
{
public HttpResponseMessage Post() { ... }
[DisableCors]
public HttpResponseMessage PutItem(int id) { ... }
}
}
Setting the methods and headers to "*" means all headers are allowed and all methods are allowed. Specific methods and headers can also be set.
Enabling CORS per Action
This is similar to enabling per controller. Except that the [EnableCors] attribute is added to actions and not the controller.
public class SampleController : ApiController
{
[EnableCors(origins: "http://client.domain", headers: "*", methods: "*")]
public HttpResponseMessage GetSample(int id) { ... }
}
Enabling CORS Globally
To enable CORS for all Web API controllers, pass an EnableCorsAttribute instance to the EnableCors method in the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("client.domain", "*", "*");
config.EnableCors(cors);
}
}
CORS can also be enabled from the Web.Config file of the project
In the web.config file. Under the <system.webserver></system.webserver>
section. Add the following changes:
<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>
<!-- New addition -->
<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 sets CORS globally for all controllers in your project.
Preflight request errors
I explained earlier what a preflight request is. In Web API, when making a preflight request from a script, more often than not, the preflight request returns a Http Error Code 405 even though everything is set right. This can be fixed by a little modification to the web.config and the Global.asax files.
In the 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" />
<!-- New addition -->
<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>
And in the Global.asax file:
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 e)
{
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.Flush();
}
}
//end of added code
}
I hope this helps you enable CORS easily on your Web API project. Cheers!
Top comments (3)
Many solutions were first, allow the content by code, but i have never imagined that most of the configurations we could do in webconfig.
Thanks.
Thanks for sharing. I like the configuration in the web.config but I couldn't make it works when I sent content-type application/json or when I sent authorization header. For POST plain text works well. In the other hand using attributes for CORS works really well with application/json and authorization header and it is not necessary configure preflight.
Thank you so much, I was looking for the right answer from the past couple of days, finally it resolved my issue. Thanks a ton!