Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core CORS request blocked; why doesn't my API apply the right headers?

Trying to set up CORS with authentication. I have a Web API site up at http://localhost:61000 and a consuming web application up at http://localhost:62000. In the Web API Startup.cs, I have:

 public void ConfigureServices(IServiceCollection services)
 {
        services.AddCors(o => o.AddPolicy("MyPolicy", corsBuilder =>
        {
            corsBuilder.WithOrigins("http://localhost:62000")
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials();
        }));
        IMvcBuilder builder = services.AddMvc();
        // ...
}

// ...

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
        app.UseCors("MyPolicy");
        app.UseDeveloperExceptionPage();
        app.UseDefaultFiles();
        app.UseStaticFiles();
        app.UseMvc();
}

All the doucmentation seems to indicate that should be all I need. In my app's Javascript, I call:

    $.ajax({
        type: 'POST',
        url: "http://localhost:61000/config/api/v1/MyStuff",
        data: matchForm.serialize(),
        crossDomain: true,
        xhrFields: { withCredentials: true },
        success: function (data) {
            alert(data);
        }
    });

And I get in Chrome: Failed to load http://localhost:61000/config/api/v1/MyStuff: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:62000' is therefore not allowed access.

...and in Firefox: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:61000/config/api/v1/MyStuff. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

What am I missing? This should be all I need to enable CORS, I thought, but clearly there is something else missing.

like image 269
Patrick Szalapski Avatar asked Apr 18 '18 12:04

Patrick Szalapski


People also ask

How do I add a CORS header in .NET core?

Set the allowed request headers AddCors(options => { options. AddPolicy(name: MyAllowSpecificOrigins, policy => { policy. WithOrigins("https://*.example.com") . AllowAnyHeader(); }); }); builder.

How do I fix CORS header Access-Control allow Origin missing?

If the server is under your control, add the origin of the requesting site to the set of domains permitted access by adding it to the Access-Control-Allow-Origin header's value. You can also configure a site to allow any site to access it by using the * wildcard. You should only use this for public APIs.


3 Answers

For ASP.NET Core 2.1 and earlier:

It seems there was an error in my code, but I got the obscure error noted instead of getting an ASP.NET-generated error page. It turns out that the CORS headers are indeed properly applied at first, but then they are stripped off any ASP.NET middleware-generated errors. See also https://github.com/aspnet/Home/issues/2378 .

I used that link to figure out this class

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace MySite.Web.Middleware
{
    /// <summary>
    /// Reinstates CORS headers whenever an error occurs.
    /// </summary>
    /// <remarks>ASP.NET strips off CORS on errors; this overcomes this issue,
    ///  explained and worked around at https://github.com/aspnet/Home/issues/2378 </remarks>
    public class MaintainCorsHeadersMiddleware
    {
        public MaintainCorsHeadersMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        private readonly RequestDelegate _next;

        public async Task Invoke(HttpContext httpContext)
        {
            // Find and hold onto any CORS related headers ...
            var corsHeaders = new HeaderDictionary();
            foreach (var pair in httpContext.Response.Headers)
            {
                if (!pair.Key.ToLower().StartsWith("access-control-")) { continue; } // Not CORS related
                corsHeaders[pair.Key] = pair.Value;
            }

            // Bind to the OnStarting event so that we can make sure these CORS headers are still included going to the client
            httpContext.Response.OnStarting(o => {
                var ctx = (HttpContext)o;
                var headers = ctx.Response.Headers;
                // Ensure all CORS headers remain or else add them back in ...
                foreach (var pair in corsHeaders)
                {
                    if (headers.ContainsKey(pair.Key)) { continue; } // Still there!
                    headers.Add(pair.Key, pair.Value);
                }
                return Task.CompletedTask;
            }, httpContext);

            // Call the pipeline ...
            await _next(httpContext);
        }
    }
}

And then I added it to my site configuration in Startup.cs:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseCors(...);
        app.UseMiddleware<MaintainCorsHeadersMiddleware>();

        ...
        app.UseMvc();
    }
like image 76
Patrick Szalapski Avatar answered Oct 26 '22 18:10

Patrick Szalapski


ASP.NET Core 2.2.0 Answer

This issue is now fixed. CORS headers are now returned even when exceptions are thrown and a 500 response is returned.

ASP.NET Core <= 2.1.0 Answer

CORS Headers were stripped from the response when an exception is thrown and a 500 response is returned.

like image 29
Muhammad Rehan Saeed Avatar answered Oct 26 '22 18:10

Muhammad Rehan Saeed


I was having this same issue on a Web API project using OWIN middleware, where a wrong package version was causing errors on the API side (hidden on the client side because CORS headers were stripped on the response, which obscured the original error). I implemented a similar solution to yours, sharing here because I could not find any similar example using OWIN on the web:

using System;
using System.Linq;
using System.Threading.Tasks;
using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Cors;

namespace App_Server
{
    using AppFunc = Func<IDictionary<string, object>, Task>;
    partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);
            app.Use(new Func<AppFunc, AppFunc>(RetainHeaders));
            ....
            (other startup tasks)
        }

        private AppFunc RetainHeaders(AppFunc next)
        {
            AppFunc appFunc = async (IDictionary<string, object> context) =>
            {
                IOwinContext httpContext = new OwinContext(context);
                var corsHeaders = new HeaderDictionary(new Dictionary<string, string[]>());

                foreach (var pair in httpContext.Response.Headers)
                {
                    if (!pair.Key.ToLower().StartsWith("access-control-")) { continue; } //not a CORS header
                    corsHeaders[pair.Key] = pair.Value.First();
                }

                httpContext.Response.OnSendingHeaders(o =>
                {
                    var localcontext = new OwinContext((IDictionary<string, object>)o);
                    var headers = localcontext.Response.Headers;
                    //make sure headers are present, and if not, add them back
                    foreach (var pair in corsHeaders)
                    {
                        if (headers.ContainsKey(pair.Key)) { continue; }
                        headers.Add(pair.Key, pair.Value);
                    }
                }, context);

                await next.Invoke(context);
            };
            return appFunc;
        }
}

This was a quite a process to work out given how poorly documented the OWIN packages are for .Net, so I hope it helps someone else who comes across it looking for a solution.

like image 43
Ryan I. Avatar answered Oct 26 '22 18:10

Ryan I.