.NET Core的UseCors()方法未添加头文件。

40

这将是 Access-Control-Allow-Origin header work? 的一个副本,但那里的方法对我也不起作用。我希望我只是遗漏了什么。

我正在尝试从我的.NET Core Web API中获得Access-Control-Allow-Origin响应头,我通过AJAX进行访问。

我已经尝试过几种方法。 除非另有说明,否则所有操作都在Startup.cs文件中进行。

方法1

根据Microsoft文档

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

方法2

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中尝试过,并获得类似的标头。
如果有关系,我正在尝试访问的方法上有一个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()
    {
        // ...
    }
}

你是如何用Postman访问服务器的?头部信息只会在OPTIONS请求中返回。 - Rob
1
那...在我看来不正确。您不应该将控制器方法标记为 'HttpOptions'。您 Method 1 中的代码看起来是正确的(根据我的本地项目进行了检查,可以正常工作)。我感觉 Chrome 失败是因为它缓存了预检 OPTIONS 请求,而 Postman 失败是因为您没有发送 OPTIONS 请求。 - Rob
请注意,204不是错误代码。204是成功代码,表示服务器没有数据。 - Rob
@Rob 是的,我知道关于204部分的问题。虽然应该有数据返回。API返回一些JSON。无论如何,它使用了选项方法。这是Chrome显示的内容:http://imgur.com/a/M2FJx - David
@Rob,谢谢你。在我的情况下,Chrome缓存了OPTIONS预检请求,禁用缓存解决了这个问题。 - msulis
显示剩余5条评论
7个回答

40

问题在于使用Bearer身份验证(或任何其他身份验证方式),会添加一个名为“Authorization”的标头,只有当设置允许该标头时,服务器才会给出响应。

有两种解决方法,下面是唯一需要的代码。它位于Web API解决方案中的Startup.cs文件的Configure()方法中。

方法1:允许所有标头

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

方法2:允许特定的请求头

app.UseCors(builder => builder.WithOrigins("https://localhost:44306")
                              .AllowAnyMethod()
                              .WithHeaders("authorization", "accept", "content-type", "origin"));
额外的标头是因为根据文档说明:
“浏览器在设置Access-Control-Request-Headers时并不完全一致。如果您将标头设置为除“*”以外的任何内容,则应至少包括“accept”、“content-type”和“origin”,以及您想要支持的任何自定义标头。”

25

仅当以下条件满足时,才会返回Access-Control-Allow-Origin头部信息:

  1. 请求包括“Origin”头部信息。
  2. 所请求的源与CORS策略匹配。

然后服务器将以源URL作为值返回ACA0头部信息。

通常由XMLHttpRequest对象设置Origin头部信息。

更多信息请参见CORS工作原理


2
这些信息对我很有帮助。我已经正确设置了一切,但在cURL测试时忘记发送“Origin”头。 - Uwe Keim
1
非常感谢您。我花了几个小时才让它正常工作,结果发现我的配置是正确的,但测试应用程序没有发送来源。 - nightblade9
谢谢,我和nightblade9遇到了相同的问题! - autlunatic
当匹配 Origin 标头时,这包括 https:// 前缀... 显然! - Roman Starkov

4
在 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 // 在Configure方法中添加以下代码

app.UseCors("AllowAllOrigins");  

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

//基于控制器启用CORS - 如果您全局应用,则不需要此项。

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

2
值得注意的是,在此添加所有来源可能是不安全的(实际上会禁用Cors),最好只指定您想要允许的来源,以避免可能发生的跨站点脚本攻击。 - Robin Wilson

4
Startup.csConfigureServices()末尾添加以下内容:
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("*"));

4
我今天在这个问题上浪费了几个小时,最后才发现是因为.Net Core 3不支持预检OPTIONS请求,如果使用端点路由RequirePolicy方法启用CORS!官方文档确实提到了这一点,但没有在明显的警告块中指出,所以我完全忽略了它。
以下内容将解决问题,但它将应用全局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();
    });
...

我并没有完全按照你说的不要做的去做,但是我现在做的这个是否也很糟糕呢?app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute().RequireAuthorization(); }); - JacobIRR

4
截至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客户端调用)开始完美地工作。


@David 如果你的 Setup.cs 文件中只有 CORS 设置,那么没有任何区别。然而,更可能的情况是你会有错误、Facebook、Swagger 等中间件。在这种情况下,启用 CORS 的函数必须是管道中的第一个/在 configure 函数中调用。否则,它可能无法正常工作。 请注意,UseMvc() 也必须在其后面,但我看到很多文章都提到了这一点。我的观点是:必须小心使用这些代码行的位置和上下文。 - VladVIII
2
我已经为此苦苦挣扎了数小时。我已经在各处看到了这种方法的详细说明,但无论我做什么都无法获得CORS头文件。我只想将这些头文件附加到每个响应中,但根据人们的说法,这是一项极其复杂的任务! - Ben Power
这对我来说就是这样。文件上传正确,但浏览器仍然报错。在配置中正确排列行解决了它。 app.UseCors("AllowAllOrigins") |> ignore; app.UseGiraffe routes - enko

2

我希望为那些可能已经按照上述建议进行操作但仍然无法正常工作的人提供一种额外的可能性。在我的情况下,由于管道中的注册顺序问题,我未能获取到标题(或仅在第一个请求时才获取到)。

我将顺序更改为:

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

转换为这样:

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

那解决了我的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接