Enable CORS in Web API 2

I have client and a server running on different ports. The server is running Web API 2 (v5.0.0-rc1).

I tried installing the Microsoft ASP.NET Web API Cross-Origin Support package and enabled it in WebApiConfig.cs. It gives me the EnableCors() function, so the package was installed correctly.

Here you can see my Register() function in WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();

    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
}

GET requests work fine. But when sending POST, I get the following:

OPTIONS http://localhost:19357/api/v1/rooms? 404 (Not Found) angular.js:10159
OPTIONS http://localhost:19357/api/v1/rooms? Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin. angular.js:10159
XMLHttpRequest cannot load http://localhost:19357/api/v1/rooms. Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.

According to Fiddler it only sends the OPTIONS request. It doesn't issue the POST afterwards.

So I'm guessing the config.EnableCors(cors); in the WebApiConfig.cs isn't doing anything, which leads to the server denying the client/browser to send a POST request.

Do you have any idea how to solve this problem?

EDIT 05.09.13 This has been fixed in 5.0.0-rtm-130905


CORS works absolutely fine in Microsoft.AspNet.WebApi.Cors version 5.2.2. The following steps configured CORS like a charm for me:

  1. Install-Package Microsoft.AspNet.WebApi.Cors -Version "5.2.2" // run from Package manager console
  2. In Global.asax, add the following line: BEFORE ANY MVC ROUTE REGISTRATIONS

    GlobalConfiguration.Configure(WebApiConfig.Register);
    
  3. In the WebApiConfig Register method, have the following code:

    public static void Register(HttpConfiguration config)
    {
        config.EnableCors();
        config.MapHttpAttributeRoutes();
    }
    

In the web.config, the following handler must be the first one in the pipeline:

<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />

In the controller derived from ApiController, add the EnableCorsAttribute:

[EnableCors(origins: "*", headers: "*", methods: "*")] // tune to your needs
[RoutePrefix("")]
public class MyController : ApiController

That should set you up nicely!


I didn't need to install any package. Just a simple change in your WebAPI project's web.config is working great:

<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="Access-Control-Allow-Origin" value="*" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Credit goes to: Using CORS in ASP.NET WebAPI Without Being a Rocket Scientist


For reference using the [EnableCors()] approach will not work if you intercept the Message Pipeline using a DelegatingHandler. In my case was checking for an Authorization header in the request and handling it accordingly before the routing was even invoked, which meant my request was getting processed earlier in the pipeline so the [EnableCors()] had no effect.

In the end found an example CrossDomainHandler class (credit to shaunxu for the Gist) which handles the CORS for me in the pipeline and to use it is as simple as adding another message handler to the pipeline.

public class CrossDomainHandler : DelegatingHandler
    {
        const string Origin = "Origin";
        const string AccessControlRequestMethod = "Access-Control-Request-Method";
        const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
        const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
        const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
        const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            bool isCorsRequest = request.Headers.Contains(Origin);
            bool isPreflightRequest = request.Method == HttpMethod.Options;
            if (isCorsRequest)
            {
                if (isPreflightRequest)
                {
                    return Task.Factory.StartNew(() =>
                    {
                        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
                        response.Headers.Add(AccessControlAllowOrigin, request.Headers.GetValues(Origin).First());

                        string accessControlRequestMethod = request.Headers.GetValues(AccessControlRequestMethod).FirstOrDefault();
                        if (accessControlRequestMethod != null)
                        {
                            response.Headers.Add(AccessControlAllowMethods, accessControlRequestMethod);
                        }

                        string requestedHeaders = string.Join(", ", request.Headers.GetValues(AccessControlRequestHeaders));
                        if (!string.IsNullOrEmpty(requestedHeaders))
                        {
                            response.Headers.Add(AccessControlAllowHeaders, requestedHeaders);
                        }

                        return response;
                    }, cancellationToken);
                }
                else
                {
                    return base.SendAsync(request, cancellationToken).ContinueWith(t =>
                    {
                        HttpResponseMessage resp = t.Result;
                        resp.Headers.Add(AccessControlAllowOrigin, request.Headers.GetValues(Origin).First());
                        return resp;
                    });
                }
            }
            else
            {
                return base.SendAsync(request, cancellationToken);
            }
        }
    }

To use it add it to the list of registered message handlers

config.MessageHandlers.Add(new CrossDomainHandler());

Any preflight requests by the Browser are handled and passed on, meaning I didn't need to implement an [HttpOptions] IHttpActionResult method on the Controller.


I'm most definitely hitting this issue with attribute routing. The issue was fixed as of 5.0.0-rtm-130905. But still, you can try out the nightly builds which will most certainly have the fix.

To add nightlies to your NuGet package source, go to Tools -> Library Package Manager -> Package Manager Settings and add the following URL under Package Sources: http://myget.org/F/aspnetwebstacknightly