ASP.NET 核心 CORS 请求被阻止;为什么我的 API 没有应用正确的 headers?
ASP.NET Core CORS request blocked; why doesn't my API apply the right headers?
正在尝试设置带有身份验证的 CORS。我在 http://localhost:61000 and a consuming web application up at http://localhost:62000 上有一个 Web API 站点。在网络 API Startup.cs 中,我有:
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();
}
所有文档似乎都表明这应该是我所需要的。在我的应用程序 Javascript 中,我调用:
$.ajax({
type: 'POST',
url: "http://localhost:61000/config/api/v1/MyStuff",
data: matchForm.serialize(),
crossDomain: true,
xhrFields: { withCredentials: true },
success: function (data) {
alert(data);
}
});
然后我进入 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.
...在 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).
我错过了什么?这应该是我启用 CORS 所需的全部,我想,但显然还缺少其他东西。
对于 ASP.NET Core 2.1 及更早版本:
我的代码似乎有错误,但我注意到了模糊的错误,而不是 ASP.NET-generated 错误页面。事实证明,CORS headers 一开始确实正确应用, 但随后它们被剥离掉任何 ASP.NET middleware-generated 错误 。另见 https://github.com/aspnet/Home/issues/2378 .
我用那个 link 来弄清楚这个 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);
}
}
}
然后我将它添加到 Startup.cs 中的站点配置中:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(...);
app.UseMiddleware<MaintainCorsHeadersMiddleware>();
...
app.UseMvc();
}
在我的例子中,CORS headers 丢失了,因为我正在发出 "application/json" 内容类型请求,在 CORS 中,这种类型的请求首先发送一个 OPTIONS 方法,然后,常规 POST 请求。但是 OPTIONS 是由我的 .Net Core 管道中的中间件代码管理的,如下所示:
if (context.Request.Method == "OPTIONS")
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
await context.Response.WriteAsync(string.Empty);
}
一旦我删除了中间件,这些请求就得到了完美的处理。
ASP.NET 核心 2.2.0 答案
此问题现在 fixed。即使抛出异常并返回 500 响应,现在也会返回 CORS headers。
ASP.NET 核心 <= 2.1.0 答案
当抛出异常并返回 500 响应时,CORS Headers 从响应中剥离。
我在使用 OWIN 中间件的 Web API 项目中遇到了同样的问题,错误的包版本导致了 API 端的错误(隐藏在客户端,因为 CORS headers 在响应中被剥离,这掩盖了原始错误)。我实现了一个与你类似的解决方案,在这里分享,因为我在网上找不到任何使用 OWIN 的类似示例:
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;
}
}
考虑到用于 .Net 的 OWIN 包的文档记录很差,这是一个相当复杂的过程,所以我希望它能帮助遇到它的其他人寻找解决方案。
正在尝试设置带有身份验证的 CORS。我在 http://localhost:61000 and a consuming web application up at http://localhost:62000 上有一个 Web API 站点。在网络 API Startup.cs 中,我有:
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();
}
所有文档似乎都表明这应该是我所需要的。在我的应用程序 Javascript 中,我调用:
$.ajax({
type: 'POST',
url: "http://localhost:61000/config/api/v1/MyStuff",
data: matchForm.serialize(),
crossDomain: true,
xhrFields: { withCredentials: true },
success: function (data) {
alert(data);
}
});
然后我进入 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.
...在 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).
我错过了什么?这应该是我启用 CORS 所需的全部,我想,但显然还缺少其他东西。
对于 ASP.NET Core 2.1 及更早版本:
我的代码似乎有错误,但我注意到了模糊的错误,而不是 ASP.NET-generated 错误页面。事实证明,CORS headers 一开始确实正确应用, 但随后它们被剥离掉任何 ASP.NET middleware-generated 错误 。另见 https://github.com/aspnet/Home/issues/2378 .
我用那个 link 来弄清楚这个 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);
}
}
}
然后我将它添加到 Startup.cs 中的站点配置中:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(...);
app.UseMiddleware<MaintainCorsHeadersMiddleware>();
...
app.UseMvc();
}
在我的例子中,CORS headers 丢失了,因为我正在发出 "application/json" 内容类型请求,在 CORS 中,这种类型的请求首先发送一个 OPTIONS 方法,然后,常规 POST 请求。但是 OPTIONS 是由我的 .Net Core 管道中的中间件代码管理的,如下所示:
if (context.Request.Method == "OPTIONS")
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
await context.Response.WriteAsync(string.Empty);
}
一旦我删除了中间件,这些请求就得到了完美的处理。
ASP.NET 核心 2.2.0 答案
此问题现在 fixed。即使抛出异常并返回 500 响应,现在也会返回 CORS headers。
ASP.NET 核心 <= 2.1.0 答案
当抛出异常并返回 500 响应时,CORS Headers 从响应中剥离。
我在使用 OWIN 中间件的 Web API 项目中遇到了同样的问题,错误的包版本导致了 API 端的错误(隐藏在客户端,因为 CORS headers 在响应中被剥离,这掩盖了原始错误)。我实现了一个与你类似的解决方案,在这里分享,因为我在网上找不到任何使用 OWIN 的类似示例:
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;
}
}
考虑到用于 .Net 的 OWIN 包的文档记录很差,这是一个相当复杂的过程,所以我希望它能帮助遇到它的其他人寻找解决方案。