ASP.NET Core CORS WebAPI:没有 Access-Control-Allow-Origin 标头

40
我已经将我的ASP.NET Core Web API部署到Azure,并可以使用Swagger或像Fiddler这样的Web调试器访问其端点。在两种情况下(在Swagger中相同的源,在计算机上使用Fiddler时不同的源),当访问API时,我得到了预期的结果。在我的Startup.cs中启用CORS如下所示:
  1. services.AddCors();添加到ConfigureServices中。

  2. 将中间件添加到Configure中:我知道这里的顺序很重要(ASP.NET 5: Access-Control-Allow-Origin in response),因此我将此调用放在方法的顶部,仅在日志记录或诊断中间件之前; 这是我的完整方法:


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

    // to serve up index.html
    app.UseDefaultFiles();
    app.UseStaticFiles();

    // http://www.talkingdotnet.com/aspnet-core-diagnostics-middleware-error-handling/
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();

    // CORS
    // https://docs.asp.net/en/latest/security/cors.html
    app.UseCors(builder =>
            builder.WithOrigins("http://localhost:4200", "http://www.myclientserver.com")
                .AllowAnyHeader()
                .AllowAnyMethod());

    app.UseOAuthValidation();
    app.UseOpenIddict();
    app.UseMvc();

    databaseInitializer.Seed().GetAwaiter().GetResult();
    env.ConfigureNLog("nlog.config");

    // swagger
    app.UseSwagger();
    app.UseSwaggerUi();
}

localhost CORS用于开发,并涉及到一个Angular2 CLI应用程序。本地CORS正常工作,我的客户端和API应用程序在同一localhost上的不同端口上,因此这是“真正”的跨源(我之所以强调这一点,是因为我在这里找到的建议:https://weblog.west-wind.com/posts/2016/Sep/26/ASPNET-Core-and-CORS-Gotchas:该文章的作者注意到响应中的CORS头仅在实际需要时发送,即在真正的跨源环境中)。

使用Fiddler我可以成功访问远程API,但我没有得到Access-Control-Allow-Origin头部信息。因此,当通过浏览器(通过我的客户端应用程序)调用API时,即使服务器返回200,AJAX请求也会失败。示例Fiddler请求(成功):

GET http://mywebapisiteurl/api/values HTTP/1.1
User-Agent: Fiddler

响应:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=3d551180c72208c1d997584c2b6119cf44e3a55c868f05ffc9258d25a58e95b1;Path=/;Domain=prinapi.azurewebsites.net
Date: Thu, 01 Dec 2016 10:30:19 GMT

["value1","value2"]

尝试访问部署在Azure上的远程API时,我的客户端应用程序总是在其AJAX请求失败并显示错误信息:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.myclientserver.com' is therefore not allowed access.

以下是使用Angular2编写的示例客户端代码(使用Plunker):
import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import { Http, Headers, Response } from '@angular/http';
import { HttpModule } from '@angular/http';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <button (click)="test()">test</button>
    </div>
  `,
})
export class App {
  name:string;
  constructor(private _http: Http) {
    this.name = 'Angular2'
  }
  public test() {
    this._http.get('http://theapisiteurlhere/api/values',
    {
        headers: new Headers({
          'Content-Type': 'application/json'
        })
    })
    .subscribe(
      (data: any) => {
        console.log(data);
      },
      error => {
        console.log(error);
      });
  }
}

@NgModule({
  imports: [ BrowserModule, HttpModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

总之,看起来ASPNET API服务器没有返回期望的CORS头,因此我的基于浏览器的客户端无法在不同的来源上托管。然而,CORS设置似乎是正确的,至少从上面引用的文档来看;我处于真正的跨源环境中;并且我将中间件放在其他中间件之前。也许我漏掉了一些显而易见的东西,但是在搜索时这些都是我找到的所有建议。有什么提示吗?
更新:
回复 @Daniel J.G:从Fiddler中的请求/响应来看是成功的:
GET http://theapiserver/api/values HTTP/1.1
User-Agent: Fiddler
Host: theapiserver
Origin: http://theappserver/apps/prin

并且:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: http://theappserver/apps/prin
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=3d551180c72208c1d997584c2b6119cf44e3a55c868f05ffc9258d25a58e95b1;Path=/;Domain=theapiserver
Date: Thu, 01 Dec 2016 14:15:21 GMT
Content-Length: 19

["value1","value2"]

Angular2(Plunker)的请求/响应失败,如报告所述。通过检查网络流量,我只能看到预检请求:
OPTIONS http://theapiserver/api/values HTTP/1.1
Host: theapiserver
Proxy-Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://run.plnkr.co
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://run.plnkr.co/h17wYofXGFuTy2Oh/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,it;q=0.6

HTTP/1.1 204 No Content
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=3d551180c72208c1d997584c2b6119cf44e3a55c868f05ffc9258d25a58e95b1;Path=/;Domain=theapiserver
Date: Thu, 01 Dec 2016 14:23:02 GMT

在此之后,请求失败并且没有更多的流量发送到服务器。报告的问题是由于响应中缺少头部信息导致的 Response to preflight request doesn't pass access control check

XMLHttpRequest cannot load http://theapiserver/api/values. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://run.plnkr.co' is therefore not allowed access.

谢谢,我更新了我的帖子(请看底部)。简而言之,使用Fiddler是可以的;但是使用Angular2时,预检请求失败,因为服务器响应中没有ACAO头,尽管启用了CORS。 - Naftis
7
大家,找到解决方法了:我之前没有注意到 Azure 门户中有一个 CORS 部分。如果我在那里不输入任何允许的来源,我的基于代码的配置似乎就完全无效了。这对我来说看起来很奇怪,因为我被迫在这里复制网址,但是一旦我添加了 * 到允许的来源中,它就可以工作了。 - Naftis
1
我猜测Azure会坐在你的应用程序前面,并进行自己的CORS检查。 - Daniel J.G.
将同样的东西放入 Azure 上的 CORS 中,这也解决了我的问题。该死。 - sensei
@Naftis,更好的做法是将您所发现的内容放入单独的答案中,并将其标记为此问题的答案。否则,在评论中很难注意到出现问题的原因。 - Yang You
显示剩余3条评论
5个回答

14

以下是我自己问题的答案,摘自评论部分:我没有注意到在Azure 门户中有一个CORS部分。如果我在那里没有输入任何允许的来源,我的基于代码的配置似乎完全无关紧要。这对我来说看起来很奇怪,因为我被迫在这里复制URL,但一旦我在允许的来源中添加了*,它就可以工作了。


我还没有测试过,但如果这是真的,那么在代码中拥有CORS配置的意义是什么?感谢Naftis指出 :) - Kien Chu
2
你刚刚节省了我数小时的烦恼!谢谢!我很感激微软在Azure门户中允许CORS配置,但令人沮丧的是它并不那么明显。这似乎完全覆盖了基于代码的CORS配置(就像其他人所说的那样)... - Ben Walters
这是不必要的,请使用 app.UseCors。 - Amorphis
1
我将 * 添加到允许的来源中,但现在我的 POST 请求会生成 500 错误,无论是在我的 Angular 应用程序还是 Postman 测试中。 - Devin Andres Salemi
这个问题现在似乎已经解决了。我可以将CORS部分留空,它会遵循我的应用程序设置。我之前做错的是像这样添加它们 http://www.example.com/,这是错误的。你必须像这样添加它们:http://www.example.com(没有尾随的正斜杠)。 - Andy
显示剩余2条评论

7

我曾遇到类似的错误,但通过按照流程保持管道的顺序来解决。(startup.cs -> 配置服务)如下:

  app.UseRouting();
  app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
  app.UseEndpoints(endpoints => .....

谢谢!那个提示结束了数小时的苦思冥想和狂饮! - James Fleming
恭喜!我已经尝试了所有的方法,但那个点救了我。 - captainblack

6
添加.AllowAnyHeader()方法可以解决您的问题。
app.UseCors(builder => builder.WithOrigins("http://localhost:4200")
                              .AllowAnyMethod()
                              .AllowAnyHeader());

2

这个错误信息有些误导人。我在尝试向DbContext添加新表后遇到了相同的错误。Angular报错"Access-Control-Allow-Origin",但Postman报错为500内部服务器错误。当尝试调用_userManager.FindByEmailAsync()时,Login方法失败了。希望这能帮助其他人。

"Original Answer"翻译成中文为"最初的回答"。


这确实帮了我很多!这个问题应该更加突出,因为在你的评论提醒我之前,我花了半天时间试图调试浏览器控制台中看起来像CORS错误的问题,结果发现我还没有将特定的数据库构件部署到生产环境中,导致API服务器出现异常。我肯定需要找到一种方法通过我正在使用的Swagger/NSwag组合更准确地展示这些错误。 - Philip Daniels

-4

WebApi 项目 ---> 在 References 上右键 ---> 在 Manage Nuget Packages 部分中搜索 Core。通过安装添加 Microsoft.AspNet.WebApi.Cors 到项目中。

在项目的 App_Start 文件夹下的 WebApi.Config 文件中添加以下代码:

var cors = new EnableCorsAttribute("","","*"); config.EnableCors(cors);

输入图像描述

输入图像描述


5
OP询问的是ASP.NET CORE,而不是ASP.NET。 - ews2001

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