How to enable CORS in ASP.net Core WebAPI

377,558

Solution 1

Because you have a very simple CORS policy (Allow all requests from XXX domain), you don't need to make it so complicated. Try doing the following first (A very basic implementation of CORS).

If you haven't already, install the CORS nuget package.

Install-Package Microsoft.AspNetCore.Cors

In the ConfigureServices method of your startup.cs, add the CORS services.

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(); // Make sure you call this previous to AddMvc
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Then in your Configure method of your startup.cs, add the following :

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // Make sure you call this before calling app.UseMvc()
    app.UseCors(
        options => options.WithOrigins("http://example.com").AllowAnyMethod()
    );

    app.UseMvc();
}

Now give it a go. Policies are for when you want different policies for different actions (e.g. different hosts or different headers). For your simple example you really don't need it. Start with this simple example and tweak as you need to from there.

Further reading : http://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/

Solution 2

  • In ConfigureServices add services.AddCors(); BEFORE services.AddMvc();

  • Add UseCors in Configure

     app.UseCors(builder => builder
         .AllowAnyOrigin()
         .AllowAnyMethod()
         .AllowAnyHeader());   
     app.UseMvc();
    

Main point is that add app.UseCors, before app.UseMvc().

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

After the above method works you can change it configure a specific ORIGIN to accept api calls and avoid leaving your API so open to anyone

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
    {
        builder.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader();
    }));

    services.AddMvc();
}

In the configure method tell CORS to use the policy you just created:

app.UseCors("ApiCorsPolicy");
app.UseMvc();

I just found this compact article on the subject - https://dzone.com/articles/cors-in-net-core-net-core-security-part-vi

Solution 3

I created my own middleware class that worked for me, i think there is something wrong with .net core middleware class

public class CorsMiddleware
{
    private readonly RequestDelegate _next;

    public CorsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public Task Invoke(HttpContext httpContext)
    {
        httpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
        httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
        httpContext.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
        httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
        return _next(httpContext);
    }
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class CorsMiddlewareExtensions
{
    public static IApplicationBuilder UseCorsMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CorsMiddleware>();
    }
}

and used it this way in the startup.cs

app.UseCorsMiddleware();

Solution 4

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {      
       app.UseCors(builder => builder
                .AllowAnyHeader()
                .AllowAnyMethod()
                .SetIsOriginAllowed((host) => true)
                .AllowCredentials()
            );
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors();
    }

Solution 5

I was struggling with this for DAYS.

I finally got it to work by moving app.UseCors(CORS_POLICY); to the TOP of Configure().

https://weblog.west-wind.com/posts/2016/sep/26/aspnet-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.

<= Even though my app didn't call UseMVC(), moving UseCors() to the top fixed the problem

Also:

  • Microsoft.AspNetCore.Cors used to be a required NuGet package in .Net Core 2 and lower; it's now automatically a part of Microsoft.AspNetCore in .Net Core 3 and higher.
  • builder.AllowAnyOrigin() and .AllowCredentials() CORS options are now mutually exclusive in .Net Core 3 and higher
  • CORS policy seems to require Angular call the server with https. An http URL seemed to give a CORS error regardless of the .Net Core server's CORS configuration. For example, http://localhost:52774/api/Contacts would give a CORS error; simply changing the URL to https://localhost:44333/api/Contacts worked.

Additional note:

In my case, CORS wouldn't work until I moved app.UseCors() above app.UseEndpoints(endpoints => endpoints.MapControllers()).

Share:
377,558

Related videos on Youtube

killerrin
Author by

killerrin

Updated on July 16, 2022

Comments

  • killerrin
    killerrin almost 2 years

    What I am trying to do

    I have a backend ASP.Net Core Web API hosted on an Azure Free Plan (Source Code: https://github.com/killerrin/Portfolio-Backend).

    I also have a Client Website which I want to make consume that API. The Client Application will not be hosted on Azure, but rather will be hosted on Github Pages or on another Web Hosting Service that I have access to. Because of this the domain names won't line up.

    Looking into this, I need to enable CORS on the Web API side, however I have tried just about everything for several hours now and it is refusing to work.

    How I have the Client Setup Its just a simple client written in React.js. I'm calling the APIs through AJAX in Jquery. The React site works so I know its not that. The Jquery API call works as I confirmed in Attempt 1. Here is how I make the calls

        var apiUrl = "http://andrewgodfroyportfolioapi.azurewebsites.net/api/Authentication";
        //alert(username + "|" + password + "|" + apiUrl);
        $.ajax({
            url: apiUrl,
            type: "POST",
            data: {
                username: username,
                password: password
            },
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (response) {
                var authenticatedUser = JSON.parse(response);
                //alert("Data Loaded: " + authenticatedUser);
                if (onComplete != null) {
                    onComplete(authenticatedUser);
                }
            },
            error: function (xhr, status, error) {
                //alert(xhr.responseText);
                if (onComplete != null) {
                    onComplete(xhr.responseText);
                }
            }
        });
    

    What I have tried


    Attempt 1 - The 'proper' way

    https://docs.microsoft.com/en-us/aspnet/core/security/cors

    I have followed this tutorial on the Microsoft Website to a T, trying all 3 options of enabling it Globally in the Startup.cs, Setting it up on every controller and Trying it on every Action.

    Following this method, the Cross Domain works, but only on a single Action on a single controller (POST to the AccountController). For everything else, the Microsoft.AspNetCore.Cors middleware refuses to set the headers.

    I installed Microsoft.AspNetCore.Cors through NUGET and the version is 1.1.2

    Here is how I have it setup in Startup.cs

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add Cors
            services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
            {
                builder.AllowAnyOrigin()
                       .AllowAnyMethod()
                       .AllowAnyHeader();
            }));
    
            // Add framework services.
            services.AddMvc();
            services.Configure<MvcOptions>(options =>
            {
                options.Filters.Add(new CorsAuthorizationFilterFactory("MyPolicy"));
            });
    
            ...
            ...
            ...
        }
    
        // This method gets called by the runtime. Use this method to configure 
        //the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env,
        ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
    
            // Enable Cors
            app.UseCors("MyPolicy");
    
            //app.UseMvcWithDefaultRoute();
            app.UseMvc();
    
            ...
            ...
            ...
        }
    

    As you can see, I am doing everything as told. I add Cors before MVC both times, and when that didn't work I attempted putting [EnableCors("MyPolicy")] on every controller as so

    [Route("api/[controller]")]
    [EnableCors("MyPolicy")]
    public class AdminController : Controller
    

    Attempt 2 - Brute Forcing it

    https://andrewlock.net/adding-default-security-headers-in-asp-net-core/

    After several hours of trying on the previous attempt, I figured I would try to bruteforce it by trying to set the headers manually, forcing them to run on every response. I did this following this tutorial on how to manually add headers to every response.

    These are the headers I added

    .AddCustomHeader("Access-Control-Allow-Origin", "*")
    .AddCustomHeader("Access-Control-Allow-Methods", "*")
    .AddCustomHeader("Access-Control-Allow-Headers", "*")
    .AddCustomHeader("Access-Control-Max-Age", "86400")
    

    These are other headers I tried which failed

    .AddCustomHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE")
    .AddCustomHeader("Access-Control-Allow-Headers", "content-type, accept, X-PINGOTHER")
    .AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Host, User-Agent, Accept, Accept: application/json, application/json, Accept-Language, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, Connection, Content-Type, Content-Type: application/json, Authorization, Connection, Origin, Referer")
    

    With this method, the Cross Site headers are being properly applied and they show up in my developer console and in Postman. The problem however is that while it passes the Access-Control-Allow-Origin check, the webbrowser throws a hissy fit on (I believe) Access-Control-Allow-Headers stating 415 (Unsupported Media Type)

    So the brute force method doesn't work either


    Finally

    Has anyone gotten this to work and could lend a hand, or just be able to point me in the right direction?


    EDIT

    So to get the API calls to go through, I had to stop using JQuery and switch to a Pure Javascript XMLHttpRequest format.

    Attempt 1

    I managed to get the Microsoft.AspNetCore.Cors to work by following MindingData's answer, except within the Configure Method putting the app.UseCors before app.UseMvc.

    In addition, when mixed with the Javascript API Solution options.AllowAnyOrigin() for wildcard support began to work as well.

    Attempt 2

    So I have managed to get Attempt 2 (brute forcing it) to work... with the only exception that the Wildcard for Access-Control-Allow-Origin doesn't work and as such I have to manually set the domains that have access to it.

    Its obviously not ideal since I just want this WebAPI to be wide opened to everyone, but it atleast works for me on a separate site, which means it's a start

    app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder()
        .AddDefaultSecurePolicy()
        .AddCustomHeader("Access-Control-Allow-Origin", "http://localhost:3000")
        .AddCustomHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE")
        .AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type, Authorization"));
    
    • Technetium
      Technetium almost 7 years
      For your 415 (Unsupported Media Type) issue, set a Content-Type request header to application/json.
    • user1007074
      user1007074 over 5 years
      Thanks for spending the time to write a such a descriptive question.
    • tala9999
      tala9999 over 5 years
      If you are testing using Postman, make sure you set Origin to * or something for the request header, then Attempt #1 should work. Without this header, Access-Control-Allow-Origin will not be returned in the response header.
    • Kirk Hawley
      Kirk Hawley over 3 years
      It was the comment below about XMLHttpRequest that did it for me, thanks!
    • Gilbert
      Gilbert over 2 years
      I am struggling with the same thing. Since yesterday. I am also trying to brute force it using my custom middleware. Its such a headache
    • Igor Mironenko
      Igor Mironenko about 2 years
      Almost glad to know others are spending hours on this - I can't get anything to work, not even a little bit, about to try some version of your brute force
  • killerrin
    killerrin almost 7 years
    XMLHttpRequest cannot load andrewgodfroyportfolioapi.azurewebsites.net/api/Authenticati‌​on. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost:3000' is therefore not allowed access. The response had HTTP status code 415.
  • killerrin
    killerrin almost 7 years
    Unfortunately that didn't work. I added the code as you/the tutorial said (in the order its listed in too), with example.com replaced with localhost:3000 and I get an error
  • killerrin
    killerrin almost 7 years
    Thanks for your help. Definitely utilized a portion of the answer to figure out the solution in the end after combining everyones answers
  • killerrin
    killerrin almost 7 years
    So with some tweaking around I got this solution to work. It looks like the problem was in using Jquery AJAX for api calls as once I switched to a pure Javascript solution using XMLHttpRequest it ended up working. I'll accept this answer and do a writeup in the main post
  • killerrin
    killerrin almost 7 years
    Thanks for your help. Tried a new solution and played with json being sent out and it worked after I stringified it and got it to work
  • Tseng
    Tseng almost 7 years
    This is unlikely going to work, when you register app.UseCors AFTER `` app.UseMvc()`. Middlewares are executed in the order they are registered
  • MrClan
    MrClan over 6 years
    using app.UseCors before app.UseMvc, in Configure method seems to work. For some reason, the sequence does seem to matter.
  • MindingData
    MindingData over 6 years
    Sorry yes. I had it wrong in original answer, changed it now. They are executed in order AND middleware can short circuit the process (So it doesn't go any further)
  • Artur Poniedziałek
    Artur Poniedziałek over 6 years
    Very elegant way of running Access-Control-Allow-Origin.
  • Rivon
    Rivon over 6 years
    Agree with Towhid that AllowAnyHeader() is needed. It let server receives OPTIONS request if HEADER's request is missing something.
  • Andrii M4n0w4R
    Andrii M4n0w4R over 6 years
    For me, the biggest problem of CORS in 2.0 is that it doesn't tell what is wrong, just silently failing to set CORS headers. Note: remember to add all your required headers to policy or it will fail (i had content-type). Also weird thing for me that CORS middleware doesn't brake request processing, just informing in logs that origin is not allowed and.. Passes request further (middleware order was correct).
  • hubert17
    hubert17 over 6 years
  • Indy-Jones
    Indy-Jones over 6 years
    This should really get more upvotes as a good "starting" point. In my experience over 25 years of coding it's always nice to know how to open up the floodgates to make sure it does in fact "work" and then close/secure things as needed.
  • Michael Brown
    Michael Brown about 6 years
    I had to enable options.DisableHttpsRequirement(); in order for any of this to work. It seems with https cors settings were not applying.
  • Felix K.
    Felix K. almost 6 years
    Just to mention this, in contrast to Configure() the order is not really important here within ConfigureServices()
  • Richard
    Richard almost 6 years
    I used the link in the Further Reader and those steps resolved this error. I wasn't sure where these changes should be placed (I thought the API). The Link confirmed that they should be placed in the API. Thanks for the help. I was totally spinning my wheels with this error.
  • Joe
    Joe almost 6 years
    This works on WebAPI and MVC and has no dependencies, Thank you!
  • Michal Frystacky
    Michal Frystacky almost 6 years
    This works with Asp.Net Core 2.1 and with more importantly with localhost
  • Jordan Ryder
    Jordan Ryder almost 6 years
    I was skeptical about this also, but it worked for me. I tried basically every other method to accomplish this that I could find on the internet, but no matter what the server would not respond with the access headers. This worked great. I'm running aspnetcore 2.1.
  • Junior Mayhé
    Junior Mayhé over 5 years
    Just for the record, sometimes Cors is not working when a client app is calling the wrong webserver URL with POST. If URL is wrong, OPTIONS will show 204 but POST may fire 404 or 500.
  • Albert221
    Albert221 over 5 years
    I recommend adding middleware that way: app.Use<CorsMiddleware>();
  • Hayha
    Hayha over 5 years
    You can replace those 2 ligne : context.Response.StatusCode = (int)HttpStatusCode.OK; await context.Response.WriteAsync(string.Empty); with a simple : return;
  • Adam
    Adam over 5 years
    To expand on your expansion of @user8266077's answer: Beware that if the request for some other reason fails, this middleware will throw an exception and the headers will not be set. Meaning that in frontend, it will still look like a CORS issue even though it's something totally different. I bypassed this by catching any exceptions in await _next(context) and setting the status code and response manually if this happens. I also had to add "authorization" to Access-Control-Allow-Headers for the preflight request to work when making requests from react that requires authorization.
  • takaz
    takaz over 5 years
    The .AllowAnyHeader() did it for me, I had issues with the preflight response.
  • Moussa Khalil
    Moussa Khalil about 5 years
    Didn't work for me until both sides were http (angular ng serve was http, core backend was https).
  • Nick De Beer
    Nick De Beer almost 5 years
    FYI - The CORS specification also states that setting origins to "*" (all origins) is invalid if the Access-Control-Allow-Credentials header is present. Meaning you cannot use AllowCredentials() with AllowAnyOrigin() like above. To use AllowCredentials() you need to set WithOrigins(). docs.microsoft.com/en-us/aspnet/core/security/…
  • Andrei Prigorshnev
    Andrei Prigorshnev almost 5 years
    You should return cors headers only if client send header "Origin" in request. In original CospMiddleware it looks like this: if (!context.Request.Headers.ContainsKey(CorsConstants.Origin)) return this._next(context);
  • Andrei Prigorshnev
    Andrei Prigorshnev almost 5 years
    Maybe "something wrong with .net core middleware class" because you just don't add header "Origin" when testing it with curl or something like this. Browsers add this header automatically when you make a request in js code.
  • Andrei Prigorshnev
    Andrei Prigorshnev almost 5 years
    See more details here developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_reques‌​ts –
  • Paul Kertscher
    Paul Kertscher almost 5 years
    Thank you very much. I nearly went nuts, asking myself why the Access-Control-Allow-Origin header was not issued by the server. Actually I sent requests via Postman w/o the Origin header. This saved my day! (Or at least my forenoon ;) )
  • HaRoLD
    HaRoLD over 4 years
    This did it for me, I originally setup my project for Windows Authentication but then had to change it to anonymous, I had CORS correctly configured but this setting in launchSettings.json was the culprit, thank you for posting this!.
  • Levi Fuller
    Levi Fuller over 4 years
    Have to update to builder.WithOrigins("http://localhost:4200").AllowAnyCredent‌​ials()... As @NickDeBeer mentioned, it won't work in 2.2 without explicitly specifying the allowed origins.
  • FoggyDay
    FoggyDay over 4 years
    NOTE: you shouldn't use Microsoft.AspNet.Cors in an ASP.Net Cor application. If you're on .Net Core 3.0 or higher, you don't need to import any NuGet package at all for CORS. If you're on .Net Core 2.3 or lower, then you need the appropriate version of Microsoft.AspNet.Cors from NuGet.
  • FoggyDay
    FoggyDay over 4 years
    This is probably a bad idea: you shouldn't mix middleware (app.UseCors()) with [EnableCors()] in the same application. You should use one or the other - but not both: docs.microsoft.com/en-us/aspnet/core/security/…: Use the [EnableCors] attribute or middleware, not both in the same app.
  • Canada Wan
    Canada Wan over 4 years
    This one should be the answer if you are using Net Core 3. Thanks for saving my life!
  • JensB
    JensB over 4 years
    .SetIsOriginAllowed((host) => true) solved it for me.
  • JensB
    JensB over 4 years
    .SetIsOriginAllowed((host) => true) solved it for me. in Configure, app.UseCores() solved it for me, nothing else worked.
  • Mark Schultheiss
    Mark Schultheiss about 4 years
    "With endpoint routing, the CORS middleware must be configured to execute between the calls to UseRouting and UseEndpoints. Incorrect configuration will cause the middleware to stop functioning correctly." here docs.microsoft.com/en-us/aspnet/core/security/…
  • Mark Schultheiss
    Mark Schultheiss about 4 years
    "With endpoint routing, the CORS middleware must be configured to execute between the calls to UseRouting and UseEndpoints. Incorrect configuration will cause the middleware to stop functioning correctly." here docs.microsoft.com/en-us/aspnet/core/security/…
  • roberto tomás
    roberto tomás about 4 years
    wow, so I fully expected any of the other answers to work before this little one with only 3 votes. but I meticulously tried each one, ... on a whim I went for yours and, it worked. thank you
  • Giox
    Giox about 4 years
    @MindingData Why I need to add UseMVC() in a pure WebAPI project? Is it necessary to load all the MVC stuff to make the CORS works??
  • sean
    sean about 4 years
    This did not work for me. I had to use the lambda AddPolicy in the services.AddCors with the allow options giving it a name. Then I just used app.UseCors("myCorsPolicy"); in configure. And then used the [EnableCors("myCorsPolicy")] attribute for the web methods I expect cors on.
  • Brian
    Brian almost 4 years
    If you do the above in core 3.1 your get the following error message "The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time. Configure the CORS policy by listing individual origins if credentials needs to be supported.'"
  • jasmintmp
    jasmintmp almost 4 years
    It didn't work for me as well, I'm hosting my front on IIS localhost:80 an backend API on IIS localhost:86 Chrome receiving error: "..from origin 'localhost:86' has been blocked by CORS policy: Request .." If I equal the ports it goes well. Tricky stuff is even if you remove you backend API Chrome is still trying to preflight OPTIONS to backend and fails with the same CORS exception instead of 404.
  • JustinHui
    JustinHui almost 4 years
    This is the one that worked for me on .NET Core 3.1 to allow pretty much anything.
  • Brandonm
    Brandonm almost 4 years
    I ran into something similar this when I upgraded from dotnet core 2.2 to 3.1. Had to move app.UseCors() above app.UseRouting(). This answer pointed me in the right direction.
  • Saurabh Rana
    Saurabh Rana over 3 years
    Thanks. This is the only solution that worked for me.The order of [code]services.AddCors();[/code] also matters.
  • smithygreg
    smithygreg over 3 years
    This also was the only one that worked for me. SignalR Asp.Net Core 3.1
  • PT2550
    PT2550 about 3 years
    Your reference to using https URL instead of http worked for me. I've been stuck for days trying to figure this out. Thanks!
  • FoggyDay
    FoggyDay about 3 years
    Glad it helped :)
  • Dan Chase
    Dan Chase almost 3 years
    @Brian something that really confuses me, is I keep seeing examples that look like code-paste's that claim to work, and I know they don't, because this is the reason I'm searching. But somehow, it must have worked for them. shrug.
  • KylianMbappe
    KylianMbappe over 2 years
    Thank you! Only solution that worked for me!
  • Rich Freeman
    Rich Freeman over 2 years
    Oh gosh - thank you (.net 5/6) - spent literally days banging my head against this one. It appears Azure implements its own CORS functionality on APIs and Websites in an App Service. This is not mentioned anywhere in any documentation I've found whilst googling. If you use Azure give this a try!
  • crazyTech
    crazyTech about 2 years
    Thanks.According to the technical docs, it says SetIsOriginAllowed "Sets the specified isOriginAllowed for the underlying policy" Could someone please elaborate in a more details as to what said method does? Thx