Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Web API CORS not working with AngularJS

I have an ASP.NET Web API running locally on some port and I have an angularjs app running on 8080. I want to access the api from the client.

I can successfully login and register my application because in my OAuthAuthorizationProvider explicitly sets the repsonse headers in the /Token endpoint.

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

That's good. However, my other API methods do not seem to work. In my WebApiCongig.Register, I enable CORS and I add the EnableCors Attribute to my controllers to allow all origins, all headers, and all methods. I can set a break point in my get method on the controller and it gets hit just fine. Here is what I found watching the Network tab in chrome.

2 requests are are sent to the same api method. One method type OPTIONS and one with method type GET. The OPTIONS request header includes these two lines

Access-Control-Request-Headers:accept, authorization

Access-Control-Request-Method:GET

And the response includes these lines

Access-Control-Allow-Headers:authorization

Access-Control-Allow-Origin:*

However, the GET method request looks quite different. It returns ok with a status code of 200, but it does not inlcude and access control headers in the request or response. And like I said, it hits the API just fine. I can even do a POST and save to the database, but the client complains about the response!!

I've looked at every single SO question and tried every combination of enabling cors. I'm using Microsoft.AspNet.Cors version 5.2.2. I'm' using AngularJS version 1.3.8. I'm also using the $resource service instead of $http which doesn't seem to make a difference either.

If I can provide more information, please let me know.

BTW, I can access the Web API using Fiddler and/or Postman by simply including the Bearer token.

like image 333
Jesse Seger Avatar asked Dec 14 '22 17:12

Jesse Seger


2 Answers

You don't seem to be handling the preflight Options requests.

Web API needs to respond to the Options request in order to confirm that it is indeed configured to support CORS.

To handle this, all you need to do is send an empty response back. You can do this inside your actions, or you can do it globally like this:

protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
    {
        Response.Flush();
    }
}

This extra check was added to ensure that old APIs that were designed to accept only GET and POST requests will not be exploited. Imagine sending a DELETE request to an API designed when this verb didn't exist. The outcome is unpredictable and the results might be dangerous.

Also I suggest enabling Cors by web.config instead of config.EnableCors(cors);

This can be done by adding some custom headers inside the <system.webServer> node.

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type" />
    <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
  </customHeaders>
 </httpProtocol>

Please note that the Methods are all individually specified, instead of using *. This is because there is a bug occurring when using *.

like image 146
Mihai Dinculescu Avatar answered Dec 17 '22 05:12

Mihai Dinculescu


This ended up being a simple fix. Simple, but it still doesn't take away from the bruises on my forehead. It seems like the more simple, the more frustrating.

I created my own custom cors policy provider attribute.

public class CorsPolicyProvider : Attribute, ICorsPolicyProvider
{
    private CorsPolicy _policy;

    public CorsPolicyProvider()
    {
        // Create a CORS policy.
        _policy = new CorsPolicy
        {
            AllowAnyMethod = true,
            AllowAnyHeader = true,
            AllowAnyOrigin = true
        };

       // Magic line right here
        _policy.Origins.Add("*");

    }

    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return Task.FromResult(_policy);
    }
}

I played around with this for hours. Everything should work right?? I mean the EnableCors attribute should work too?? But it didn't. So I finally added the line above to explicitly add the origin to the policy. BAM!! It worked like magic. To use this just add the attribute to your api class or method you want to allow.

[Authorize]
[RoutePrefix("api/LicenseFiles")]
[CorsPolicyProvider]
//[EnableCors(origins: "*", headers: "*", methods: "*")] does not work!!!!!  at least I couldn't get it to work
public class MyController : ApiController
{
like image 42
Jesse Seger Avatar answered Dec 17 '22 06:12

Jesse Seger