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

孤者浪人 提交于 2019-11-30 14:15:53

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();
    }

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.

In my case the CORS headers were lost because I was making an "application/json" content type request, and in CORS this type of request send first an OPTIONS method, after that, the regular POST is requested. But the OPTIONS was been managed by a Middleware code in my .Net Core pipeline with something like this:

        if (context.Request.Method == "OPTIONS")
        {
            context.Response.StatusCode = (int)HttpStatusCode.OK;
            await context.Response.WriteAsync(string.Empty);
        } 

Once I remove the middleware those requests were flawless attended.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!