.NET Core UseCors() 不添加 headers

.NET Core UseCors() does not add headers

这将是 How does Access-Control-Allow-Origin header work? 的副本,但那里的方法也不适合我。我希望我只是遗漏了一些东西。

我正在尝试从我的 .NET Core Web API 的响应中获得 Access-Control-Allow-Origin header,我正在通过 AJAX.[=22 访问它=]

我已经尝试了几种方法。所有,除非另有说明,都在 Startup.cs 文件中。

方法一

根据 Microsoft Documentation:

public void ConfigureServices(IServiceCollection services)
{
    // Add database
    services.AddDbContext<DbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DbConnection")));

    // Add the ability to use the API with JSON
    services.AddCors();

    // Add framework services.
    services.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            serviceScope.ServiceProvider.GetService<DbContext>().Database.Migrate();
            serviceScope.ServiceProvider.GetService<DbContext>().EnsureSeedData();
        }
    }

    app.UseCors(builder => builder.WithOrigins("https://localhost:44306").AllowAnyMethod());

    app.UseJwtBearerAuthentication(new JwtBearerOptions
    {
        Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
        Audience = Configuration["Authentication:AzureAd:Audience"],
    });

    app.UseMvc();
}

方法二

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddCors(options => options.AddPolicy("AllowWebApp",
        builder => builder.AllowAnyMethod()
                          .AllowAnyMethod()
                          .AllowAnyOrigin()));
                          //.WithOrigins("https://localhost:44306")));

    // ...
}

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

    app.UseCors("AllowWebApp");

    // ...
}

我也尝试在控制器和方法上添加 [EnableCors("AllowWebApp")]

从 Postman 那里,我得到:

content-encoding → gzip
content-type → text/plain; charset=utf-8
date → Wed, 25 Jan 2017 04:51:48 GMT
server →Kestrel
status → 200
vary → Accept-Encoding
x-powered-by → ASP.NET
x-sourcefiles → =?UTF-8?B?[REDACTED]

我也在 Chrome 中尝试过,得到了类似的 headers。

如果重要的话,我尝试访问的方法有一个 Authorize 属性。但是那部分应该工作正常(我至少得到了很好的回应)

那么,我是不是遗漏了一些非常明显的东西,还是它坏了?我目前 运行 版本 1.1.0.


编辑添加 JS 和控制器存根

function getContactPreviews(resultsCallback) {
    var xmlhttp = new XMLHttpRequest();

    xmlhttp.onreadystatechange = () => {
        if (xmlhttp.readyState == XMLHttpRequest.DONE && xmlhttp.status == 200) {
            resultsCallback(JSON.parse(xmlhttp.response));
        }
    }

    xmlhttp.open("GET", "https://localhost:44357/api/User/ContactsPreview", true);
    xmlhttp.setRequestHeader("Authorization", "Bearer " + localStorage.getItem("AuthorizationToken"));
    xmlhttp.send();
}

控制器存根

[Authorize]
[Route("api/[controller]")]
public class UserController : ApiController
{
    [HttpGet(nameof(ContactsPreview))]
    [EnableCors("AllowWebApp")]
    public IEnumerable<Customer> ContactsPreview()
    {
        // ...
    }
}

问题是,当使用 Bearer 身份验证(或任何我想象的)时,它会添加一个 header "Authorization",并且服务器只会在设置允许的情况下给出一个好的 header.

有两种方法可以解决这个问题,下面是只需要代码。它进入 Web API 解决方案中 Startup.cs 中的 Configure() 方法。

方法一:允许所有headers

app.UseCors(builder => builder.WithOrigins("https://localhost:44306")
                                .AllowAnyMethod()
                                .AllowAnyHeader());

方法二:允许特定的headers

app.UseCors(builder => builder.WithOrigins("https://localhost:44306")
                              .AllowAnyMethod()
                              .WithHeaders("authorization", "accept", "content-type", "origin"));

额外的 header 是因为,根据文档:

Browsers are not entirely consistent in how they set Access-Control-Request-Headers. If you set headers to anything other than "*", you should include at least "accept", "content-type", and "origin", plus any custom headers that you want to support.

Access-Control-Allow-Origin header 仅在以下情况下返回:

  1. 请求包含 "Origin" header.
  2. 请求的来源符合 CORS 政策。

然后服务器 returns ACAO-header 以来源 URL 作为值。

Origin header 通常由 XMLHttpRequest object 设置。

有关详细信息,请参阅 How CORS works

在 Startup.cs 文件中,添加以下内容

public CorsPolicy GenerateCorsPolicy(){
                var corsBuilder = new CorsPolicyBuilder();
                corsBuilder.AllowAnyHeader();
                corsBuilder.AllowAnyMethod();
                corsBuilder.AllowAnyOrigin(); // For anyone access.
                //corsBuilder.WithOrigins("http://localhost:56573"); // for a specific url. Don't add a forward slash on the end!
                corsBuilder.AllowCredentials();
                return corsBuilder.Build();
    }

在 ConfigureServices 方法中:

 services.AddCors(options =>
                {
                    options.AddPolicy("AllowAllOrigins", GenerateCorsPolicy());
                });

// 在整个应用程序中全局应用 CORS // 在配置方法中,添加

app.UseCors("AllowAllOrigins");  

[禁用Cors]
使用 DisableCors 属性,我们可以为控制器或操作禁用 CORS。

//启用 CORS 控制器基础 - 如果你在全球范围内申请,你不需要这个。

[EnableCors("AllowAllOrigins")]  
public class HomeController: Controller {}  

截至 2019 年 3 月 17 日,.NET Core 版本 2.1:

这可能会为其他可怜的人节省一些时间...在某些时候,我开始感到沮丧,几乎放弃了将 .NET Core WebApi 作为一个单独的项目。

在现实生活中,启动函数中还有其他配置,例如我有 Swagger、DI 注册等。直到我将 AddCors() 和 UseCors() 方法都放在配置函数中第一个被调用的方法之前,我无法使该死的东西工作。

 // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("SomePolicy",
                    builder => builder.AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
            });



 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseCors("SomePolicy");

之后,来自 Angular 6 应用程序的调用(Swagger Typescript 客户端调用)开始发挥作用。

我想为那些可能已遵循上述建议但仍然无效的人增加一种可能性。在我的例子中,由于管道中的注册顺序,我没有得到 header 返回(或仅在第一次请求时得到它)。

我更改了顺序:

app.UseResponseCaching();
app.UseHttpsRedirection();
app.UseCors("WideOpen");
app.UseMvc();

为此:

app.UseCors("WideOpen");
app.UseResponseCaching();
app.UseHttpsRedirection();
app.UseMvc();

这解决了我的问题。

我今天在这个问题上浪费了 小时,才发现这是因为如果您使用启用 CORS 的端点路由 RequirePolicy 方法,.Net Core 3 不支持预检 OPTIONS 请求!

official documentation确实提到了这一点,但没有在明显的警告块中指出,所以我完全错过了。

以下将解决该问题,但它会应用全局 CORS 策略,所以买者自负!

服务配置:

public void ConfigureServices(IServiceCollection services)
{
    string[] corsOrigins = Configuration.GetSection("AllowedHosts").Get<string[]>();

    services.AddCors(options =>
    {
        options.AddPolicy("AllowConfiguredOrigins", builder => builder
            .WithOrigins(corsOrigins)
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials()
        );
    });
...

基本上,不要这样做:

public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();
    app.UseCors();    
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers().RequireCors("AllowConfiguredOrigins");
    });
...

...改为这个

配置()

public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();
    app.UseCors("AllowConfiguredOrigins");
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
...

Startup.cs 的末尾 ConfigureServices() 添加:

services.AddCors();

然后在 Configure() 顶部 添加:

app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("*"));

// alternatively you could use .With... methods to specify your restrictions:
// app.UseCors(x => x.WithOrigins("http://domain1.com").WithMethods("GET","POST").WithHeaders("Authorization").WithExposedHeaders("*"));