Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

ASP.NET Core and CORS Gotchas


Interconnected

Last night I was working on updating my ASP.NET Core AlbumViewer sample application to Angular 2.0 and in the process ran into CORS problems. Angular 2.0's default working environment runs a development server off a seperate port which is effectively a seperate domain and all calls back to the main ASP.NET site for the API calls effectively are cross domain calls. Alas those calls failed and upon closer inspection it was due to the fact that the CORS headers weren't getting sent.

CORS Setup in ASP.NET Core

CORS is a server based mechanism that essentially lets a server say:

I allow cross domain calls from the domains I specify

It's good to be king, huh? (especially a king with no clothes since the protocol does next to nothing to prevent malicious attacks but that's a story for another post)

When browsers make cross domain calls using XHR, they request CORS headers to decide whether the target server allows access to the source domain. In this case the source domain is Angular's dev server (localhost:3000) and the target server is my ASP.NET API service (localhost:5000 (raw Kestrel) or localhost/albumviewer (IIS/IIS Express)).

To set up CORS is at least a 3 step process:

  • You register CORS functionality
  • You configure CORS options
  • You apply the functionality

There are a number of different ways to do this but by far the best approach IMHO is to create a CORS policy and then apply that policy either globally to all requests or specific controllers.

I like the policy approach because:

  • It allows me to add CORS and declare the policy in one place
  • It allows the policy to be reused and be applied selectively

Below I use the explicit policy approach.

Register and Define a Policy

To do this start with registering CORS functionality in ConfigureServices() of Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // Add service and create Policy with options
    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials() );
    });
    
    
    services.AddMvc(); 
}

The AddCors() call above adds the CORS features to ASP.NET and creates a custom policy that can be reused in the application by name. There are other ways to do essentially the same thing by explicitly adding a policy builder in the configuration step but to me this seems cleanest - define one or more policies up front and then apply it.

Apply the Policy

Once the policy has been defined it can be applied.

You can apply the policy globally to every request in the application by call app.useCors() in the Configure() method of Startup:

public void Configure(IApplicationBuilder app)
{
    // ...

    // global policy - assign here or on each controller
    app.UseCors("CorsPolicy");

    // ...
    
    // IMPORTANT: Make sure UseCors() is called BEFORE this
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

UseCors() has to be called before UseMvc()

Make sure you declare the CORS functionality before MVC so the middleware fires before the MVC pipeline gets control and terminates the request.

or you can apply the policy to individual controllers:

[EnableCors("CorsPolicy")]
[ApiExceptionFilter]
public class AlbumViewerApiController : Controller

Check that it works

When it's all said and done you now should get the appropriate CORS headers with your response:

HTTP/1.1 200 OK
Date: Tue, 27 Sep 2016 07:09:08 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:3000
Content-Length: 2851

Note that the actual headers sent may vary depending on what your request needs. GET operations might have different CORS headers than a POST or OPTION request.

Watch out for testing CORS without Cross Domain!

So last night I reviewed my code and checked for the CORS functionality. It turns out I had at some point renamed the policy and so the policy strings were out of sync resulting in mismatched policy names and no CORS headers. Once I fixed that it seemed that everything should be well.

I went ahead and tested API calls through a browser and found: No CORS headers. Checked all again for errors and it all looked just fine. WTH? It wasn't until I tried running the full Angular application again that I found that the app was now working, and the CORS headers were being sent properly. In this duh! moment I realized of course that this is the correct behavior.

CORS headers are only sent on cross domain requests and the ASP.NET CORS module is smart enough to detect whether a same domain request is firing and if it is, doesn't send the headers. Duh - of course, but in the heat of the moment I totally didn't think of that.

The sad thing is this is not the first time I've made this mistake :-) As soon as I figured it out, I realized I had made that very same mistake a few months earlier when I set up the original CORS functionality and tested - and failed/flailed. History repeats itself. To avoid that - I'm writing it down to jog my memory.

So, if you're going to test CORS operation the only effective way to do is is by using a cross-site origin to trigger the CORS behavior. Either use a cross site client app (my Angular app in this case) or an HTTP test client (I used my West Wind WebSurge tool with an explicit origin header) and avoid scratching your head on why the CORS headers are not showing up.

Resources

Posted in ASP.NET Core  ASP.NET  Security  

The Voices of Reason


 

Fabian Gosebrink
September 26, 2016

# re: ASP.NET Core and CORS Gotchas

Thanks for this post. I was looking into this a few days ago. Found this solution: https://github.com/FabianGosebrink/ASPNET-Core-Angular2-StarterTemplate/blob/master/src/ASPNETCoreAngular2Demo/Startup.cs

I was wondering about the many ways to enable CORS and I did not know which one was "right" or "wrong". The only difference between those two solutions is that yours has a specific name which can be excplicitly applied in the
app.UseCors("..."); 

Method.

Or are there any other differences?

BR

Fabian

Rick Strahl
September 26, 2016

# re: ASP.NET Core and CORS Gotchas

@Fabian - yup it's confusing to have a number of different ways to do this. If you look at the actual ASP.NET docs for CORS you see the confusion quite clearly as they try to explain it all in a long and confusing topic.

I think under the covers it all goes into a builder - the different mechanisms are just different ways to create and cache that builder. I like the explicit policy approach because:

* It allows me to add CORS and declare the policy in one place
* It allows the policy to be reused and be applied selectively

Fabian Gosebrink
September 26, 2016

# re: ASP.NET Core and CORS Gotchas

Okay...just needed that to check if my thoughts are correct. Thank you :)

Kristian Hellang
September 26, 2016

# re: ASP.NET Core and CORS Gotchas

> Make sure you declare the CORS functionality before > MVC as the headers have to be applied before MVC completes the request.

AddCors and AddMvc has nothing to do with requests. They're simply registering the required services in the IoC container. There might be some other reason for why you should do this, but the one listed is definitely not one :)

-- Kristian

Rick Strahl
September 27, 2016

# re: ASP.NET Core and CORS Gotchas

@Kristian - the middleware *order* definitely matters, although you may be right that it has the important order is in the Configure() method, not ConfigureServices(). To be safe I make sure I have the CORS definition before MVC in both places. Checking now to see which one actually matters, or not at all. Several references mention this explicitly.

Later: Ok - so I checked and you're correct: If you do this in *Configure()*:

app.UseMvc(routes =>
{
      routes.MapRoute(
                 name: "default",
                  template: "{controller=Home}/{action=Index}/{id?}"
      );
});
 
app.UseCors("CorsPolicy");


It doesn't work. It works only if you declare the policy first (or use the policy on the controller).

Thanks for pointing that out - I'll update the post.

Alexander
October 19, 2016

# re: ASP.NET Core and CORS Gotchas

Also works for my SignalR server, thanks!

Datum Geek
December 13, 2016

# re: ASP.NET Core and CORS Gotchas

i was pulling out my hair, going around and around... getting depressed, my beautiful aspnet core webapi - was there no hope??? cors just wouldn't work. no header was returned by the service...

then, FINALLY, i remembered that i once had a problem where an unhandled exception within my webapi method produced the same behavior. this could happen to you. beware of these exceptions whose error masquerades as a missing cors header !!!!!!!!!!!!!!


Jason
January 28, 2017

# re: ASP.NET Core and CORS Gotchas


James
February 25, 2017

# re: ASP.NET Core and CORS Gotchas

This only seems to work for GET requests based on my testing.


Rick Strahl
February 26, 2017

# re: ASP.NET Core and CORS Gotchas

Make sure you add .AddAnyMethod() which is required for it to work with non-GET requests.


Alex
March 06, 2017

# re: ASP.NET Core and CORS Gotchas

That seems to work with Kestrel. But not with IISExpress. Still getting 401.


Daniel U.
March 10, 2017

# re: ASP.NET Core and CORS Gotchas

I followed you implementation. Unfortunality i got Errors like the "URL" is not found in Access-Control-Allow-Origin-Header

I use a JQuery Ajax get request to load an rss feed.

If i use Chrome with Cors extension enabled it works fine. Is it deaktivated or i use another browser i got Errors.

What did i wrong.

Using ASP.net Core with Kestrel


Rick Strahl
March 11, 2017

# re: ASP.NET Core and CORS Gotchas

@Alex - works for me on IIS Express and IIS. There is no reason that the behavior should be any different on a different Web Server because in the end it's just HTTP headers that get injected and the Kestrel is the one doing it regardless of whether IIS Express sits in front of it or not. There must be something else going on. A 401 is not found which I think is different than a CORS error.

@Daniel - What's the exact error message from jquery for this? This really shouldn't be a jquery issue, but a browser issue since the brower's XHR determines the CORS viability.


Ian Buchan
March 15, 2017

# re: ASP.NET Core and CORS Gotchas

I have intermittently ended up on this blog multiple times per month throughout my career in the past 4 years. The advice has always been top notch, covering some of my biggest concerns, with detailed information and quality references.

This post basically covered the exact issue I'm facing with my Angular 2 app hosted through the webpack-dev-server and locally instantiated ASP.NET Core Web API in Kestrel; worked like a charm. Rick Strahl, high five man, you're freaking awesome.

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017