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.
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 *
.
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
{
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With