在 .NET Core Web API 上为 CORS 启用 OPTIONS 标头

Posted

技术标签:

【中文标题】在 .NET Core Web API 上为 CORS 启用 OPTIONS 标头【英文标题】:Enable OPTIONS header for CORS on .NET Core Web API 【发布时间】:2017-07-01 04:26:09 【问题描述】:

我在 *** 上找不到解决方案后解决了这个问题,所以我在这里分享我的问题并在答案中分享解决方案。

使用 AddCors 在我的 .NET Core Web Api 应用程序中启用跨域策略后,它仍然无法在浏览器中运行。这是因为浏览器(包括 Chrome 和 Firefox)会首先发送一个 OPTIONS 请求,而我的应用程序只会以 204 No Content 响应。

【问题讨论】:

失败的具体情况是什么?如果它是“对于任何执行 CORS 的 chrome/ff 浏览器来说它总是失败”,那么框架怎么还没有涵盖呢?似乎这将是一个相当大的遗漏。 我同意。然而,事情就是这样。该框架将允许您使用内置功能执行 CORS,但它不处理 OPTIONS 调用,这是正常使用来自浏览器的跨域 api 调用的要求。但是,您可以通过进行更简单的调用来避免它,例如将类型设置为 text/plain 和其他一些东西。那么浏览器就不会先调用 OPTIONS 了。 IIS 应该是处理这些事情的人,所以任何在 2017 年 11 月之后阅读本文的人都应该使用 IIS CORS 模块,blogs.iis.net/iisteam/introducing-iis-cors-1-0 是或在 Azure App Service 上,似乎在过去几年中,在 Azure 中设置 CORS 并允许某些域或所有域 (*) 就足够了。当 CORS 中没有配置域时,上述内容将在 Azure 上运行,这显然意味着 Azure 让应用程序自己处理 CORS。 【参考方案1】:

在您的项目中添加一个中间件类来处理 OPTIONS 动词。

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

namespace Web.Middlewares

    public class OptionsMiddleware
    
        private readonly RequestDelegate _next;

        public OptionsMiddleware(RequestDelegate next)
        
            _next = next;
        

        public Task Invoke(HttpContext context)
        
            return BeginInvoke(context);
        

        private Task BeginInvoke(HttpContext context)
        
            if (context.Request.Method == "OPTIONS")
            
                context.Response.Headers.Add("Access-Control-Allow-Origin", new[]  (string)context.Request.Headers["Origin"] );
                context.Response.Headers.Add("Access-Control-Allow-Headers", new[]  "Origin, X-Requested-With, Content-Type, Accept" );
                context.Response.Headers.Add("Access-Control-Allow-Methods", new[]  "GET, POST, PUT, DELETE, OPTIONS" );
                context.Response.Headers.Add("Access-Control-Allow-Credentials", new[]  "true" );
                context.Response.StatusCode = 200;
                return context.Response.WriteAsync("OK");
            

            return _next.Invoke(context);
        
    

    public static class OptionsMiddlewareExtensions
    
        public static IApplicationBuilder UseOptions(this IApplicationBuilder builder)
        
            return builder.UseMiddleware<OptionsMiddleware>();
        
    

然后将app.UseOptions(); this 添加为 Startup.cs 的 Configure 方法的第一行。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

    app.UseOptions();

【讨论】:

我确实做了这件事,并且可以获得访问中间件的请求,但它返回“无法访问请求的 URL 服务可能暂时关闭或它可能已永久移动到新网址。[chrome 套接字错误]:连接已重置(对应于 TCP RST)”错误。我做错了什么? 我不知道。为了解决问题,我会打开 Fiddler 并检查请求和响应的详细信息。 +1,但是有一点需要调整,如果method是options就不要调用_next.Invoke,调用context.Response.WriteAsync("OK");之后应该结束请求,所以把Invoke的实现改成:@ 987654326@ 这对于带有 .NET Core 后端的 Angular 6 前端非常有效。这与允许以下内容的“AddCors”一起使用:AllowAnyOrigin、AllowAnyMethod、AllowAnyHeader 和 AllowCredentials 可以正常工作。我们的 API 现在是完全公开的。我们必须将 X-Auth 标头添加到 OPTIONS 中间件,但这是因为我们使用从客户端到后端的 X-Auth。 @AmenAyach 无关紧要,因为在 if 语句中它返回 context.Response.WriteAsync("OK"); 所以如果方法是 OPTIONS 则永远不会到达 _next.invoke(context)【参考方案2】:

我知道它已得到答复。只是回答更新的信息。所以它会帮助别人。

它现在已内置到 ASP.NET Core 框架中。

只要关注https://docs.microsoft.com/en-us/aspnet/core/security/cors

替换

    app.UseCors(builder =>
   builder.WithOrigins("http://example.com"));

        app.UseCors(builder =>
       builder.WithOrigins("http://example.com")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials());

【讨论】:

有效吗?问题是 Chrome 首先发送了一个“OPTIONS”调用并且没有得到批准,所以真正的调用永远不会来自 Chrome 浏览器。这就是“AllowAnyMethod”解决的问题吗?编辑:我现在看到这正是他们在您链接到的文章中提到的内容! fyi:如果您不想允许 any 标头或方法,可以使用方法 WithHeaders/WithMethods 通过这样做我得到204 No Content作为响应状态码 204 No Content 有问题吗?似乎您应该从 OPTIONS 请求中获得的唯一信息是在标头中。不? Mozilla seems to agree. 这需要进入Configure() in Startup.cs【参考方案3】:

这对我有用:

请确保:

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

在以下任何一项之前发生:

app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCookiePolicy();

请记住,我们正在处理“管道”。 cors 的东西必须放在第一位。

-gimzani

【讨论】:

问题是这些设置是否适用于 OPTIONS 调用 谢谢,这对我有用。我想强调一下,正如@NielsBrinch 所说,cors 的东西必须在任何其他 HTTP 请求管道配置方法之前。 我想知道为什么 IDE 或服务器没有关于此类错误的警告。这些都是简单的陷阱,毫无戒心的开发人员可能会发现自己陷入并花费额外的时间来尝试调试。我猜 *** 帮助遏制了很多开发人员的抑郁症。【参考方案4】:

不需要额外的中间件。如上所述,唯一需要的是 Cors 配置中允许的 OPTIONS 方法。 您可以按照此处的建议 AllowAnyMethod:https://***.com/a/55764660/11921910

但只允许这样的特定内容更安全:

app.UseCors(builder => builder
.WithOrigins("https://localhost", "https://production.company.com") /* list of environments that will access this api */
.WithMethods("GET", "OPTIONS") /* assuming your endpoint only supports GET */
.WithHeaders("Origin", "Authorization") /* headers apart of safe-list ones that you use */
);

某些标题总是被允许的: https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header

【讨论】:

【参考方案5】:

AspNetCoreModuleV2 OPTIONS 问题

.Net 核心模块不知道如何处理导致预检 CORS 问题的 OPTIONS,因此解决方案是将 OPTIONS 动词排除在其处理之外。它是通过将 * 替换为您想要的动词来完成的,除了 OPTIONS。不用担心,OPTIONS 动词会被默认加载的 OPTIONSHandler 处理:

IIS

解决方法:修改web.config

 <add name="aspNetCore" path="*" verb="* modules="AspNetCoreModuleV2" resourceType="Unspecified" />

把它变成这样:

<add name="aspNetCore" path="*" verb="GET,POST,PUT,DELETE" modules="AspNetCoreModuleV2" resourceType="Unspecified" />

IIS Express:适用于 Visual Studio 调试器

我尝试修改.vs\ProjectName\config\applicationhost.config 在文件的底部,但没有希望。因此,在这种特定情况下,您可以使用选择的答案。

【讨论】:

【参考方案6】:

我想为单个方法允许此操作,而不是使用中间件在任何方法上允许此操作。这就是我结束做的事情:

'OPTIONS' 方法的手动处理

[HttpOptions("/find")]
public IActionResult FindOptions()

    Response.Headers.Add("Access-Control-Allow-Origin", new[]  (string)Request.Headers["Origin"] );
    Response.Headers.Add("Access-Control-Allow-Headers", new[]  "Origin, X-Requested-With, Content-Type, Accept" );
    Response.Headers.Add("Access-Control-Allow-Methods", new[]  "POST, OPTIONS" ); // new[]  "GET, POST, PUT, DELETE, OPTIONS" 
    Response.Headers.Add("Access-Control-Allow-Credentials", new[]  "true" );
    return NoContent();

[HttpPost("/find")]
public async Task<IActionResult> FindOptions([FromForm]Find_POSTModel model)

    AllowCrossOrigin();
    
    // your code...


private void AllowCrossOrigin()

    Uri origin = null;
    Uri.TryCreate(Request.Headers["Origin"].FirstOrDefault(), UriKind.Absolute, out origin);

    if (origin != null && IsOriginAllowed(origin))
        Response.Headers.Add("Access-Control-Allow-Origin", $"origin.Scheme://origin.Host");

当然,你可以随意实现IsOriginAllowed

private bool IsOriginAllowed(Uri origin)

    const string myDomain = "mydomain.com";
    const string[] allowedDomains = new [] "example.com", "sub.example.com" ;

    return 
           allowedDomains.Contains(origin.Host) 
           || origin.Host.EndsWith($".myDomain");


您可以在how to enable CORS for POST requests on a single endpoint找到更多详细信息

【讨论】:

【参考方案7】:

实际上,没有一个答案对我有用,但我终于弄清楚了问题所在,我不敢相信 我刚搬了app.UserCors("PolicyName"); 之前app.UseAuthorization(); 它开始工作了!

我认为这可能对某人有所帮助。

services.AddCors(options =>

  options.AddPolicy("EnableCORS", bl =>
  
    bl.WithOrigins(origins)
      .AllowAnyMethod()
      .AllowAnyHeader()
      .AllowCredentials()
      .Build();
  );
);


..........................
app.UseAuthentication();
app.UseCors("EnableCORS");
.....
app.UseAuthorization();

【讨论】:

【参考方案8】:

我想针对我在本地测试 api 和客户端 Web 应用程序的具体情况给出具体答案。我知道这是一个较晚的条目,但 CORS 在 dot net core 中发生了很大变化,我想,像我这样的新手可能会从完整的帖子中受益。

对我来说,这是两个背靠背发生的问题。

    CORS 拒绝错误 以及 Firefox 上的 OPTIONS 问题(我认为 chrome 也会这样做) 我的 API 也在运行 HTTPS 网络应用没有 HTTPS 它们都在本地运行,为了清楚起见再次提及。

首先,这是 public void ConfigureServices(IServiceCollection services)

        //lets add some CORS stuff 
        services.AddCors(options =>
        
            options.AddDefaultPolicy(builder => 
                builder.WithOrigins("http://localhost:3000",
                                    "http://www.contoso.com");
                builder.AllowAnyMethod();
                builder.AllowAnyHeader();
                builder.AllowCredentials();
            );
        );

然后,这个,去,public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

  app.UseCors();

【讨论】:

【参考方案9】:

欢迎。

[HttpOptions("/find")] public IActionResult FindOptions()


    Response.Headers.Add("Access-Control-Allow-Origin", new[]  (string)Request.Headers["Origin"] );
    Response.Headers.Add("Access-Control-Allow-Headers", new[]  "Origin, X-Requested-With, Content-Type, Accept" );
    Response.Headers.Add("Access-Control-Allow-Methods", new[]  "POST, OPTIONS" ); // new[]  "GET, POST, PUT, DELETE, OPTIONS" 
    Response.Headers.Add("Access-Control-Allow-Credentials", new[]  "true" );
    return NoContent();
`enter code here`

【讨论】:

此问题已包含多个答案和一个已接受的答案。您能解释一下您的答案与其他答案的不同之处吗?也知道从长远来看,仅代码的答案没有用处。

以上是关于在 .NET Core Web API 上为 CORS 启用 OPTIONS 标头的主要内容,如果未能解决你的问题,请参考以下文章

带有 Javascript 的 Dot Net Core Web API - CORS 策略错误

创建 ASP.NET Core Web API 后端容器,包括为前端恢复的数据库

在 Manjaro 上为 Rider IDE 安装 .NET core 2.0.0

如何在 Windows 机器上为 Linux 编译 .NET Core 应用程序

将文件从 ASP.NET Core Web api 发布到另一个 ASP.NET Core Web api

在 Kestrel 上为 ASP.NET Core 上的两个不同端点发布两个不同的端点