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

时间:2016-12-01 11:02:27

标签: c# azure angular asp.net-core cors

我已将ASP.NET核心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),因此我将此调用放在方法的顶部,只是在记录之前或诊断中间件;这是我的完整方法:

  3. 
    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应用程序位于同一本地主机上的不同端口上,所以这是“真正的”交叉起源(我正在评论这个,因为我在这里找到的建议:https://weblog.west-wind.com/posts/2016/Sep/26/ASPNET-Core-and-CORS-Gotchas:该帖子的作者注意到响应中的CORS头仅在实际需要时发送 ,即在真正的跨源环境中。)

    使用Fiddler我可以成功访问远程API,但我得到NO 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:来自提琴手的请求/回复是成功的:

    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.
    

6 个答案:

答案 0 :(得分:8)

以下是我自己的问题的答案,从评论中复制:我没有注意到在 Azure门户中有一个CORS部分。如果我没有在那里输入任何允许的来源,我的基于代码的配置似乎完全无关紧要。这对我来说很奇怪,因为我不得不在这里复制网址,但是一旦我将*添加到允许的来源,就会有效。

答案 1 :(得分:3)

添加.AllowAnyHeader()方法可以解决您的问题

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

答案 2 :(得分:2)

我遇到了类似的错误,但通过保持管道井然有序,我的错误得到了解决。 (startup.cs -> configureServices) 喜欢

import sys
import copy
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *


data = [['a','b','c','x','y'],['d','e','f','x','y'],['g','h','i','x','y']]

class TableModel(QAbstractTableModel):
    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            # See below for the nested-list data structure.
            # .row() indexes into the outer list,
            # .column() indexes into the sub-list
            return self._data[index.row()][index.column()]

    def setData(self, index, value, role=Qt.EditRole):
        if role == Qt.EditRole:
            row = index.row()
            column = index.column()
            self._data[row][column] = value
            self.dataChanged.emit(index, index)
            return True
        return QAbstractTableModel.setData(self, index, value, role)

    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

    def columnCount(self, index):
        # The following takes the first sub-list, and returns
        # the length (only works if all rows are an equal length)
        return len(self._data[0])

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.selection = data[0]
        # buil UI
        self.init_ui()

    def init_ui(self):
        # layout
        self.box_window = QVBoxLayout()
        # content
        self.invisible_table = QTableView()
        tmp = copy.deepcopy(data)
        tmp.insert(0,self.selection)
        self.model = TableModel(tmp)
        self.invisible_table.setModel(self.model)
        self.invisible_table.setSelectionBehavior(QTableWidget.SelectRows)
        self.invisible_table.horizontalHeader().hide()
        self.invisible_table.horizontalHeader().setMinimumSectionSize(100)
        self.invisible_table.verticalHeader().hide()
        # self.invisible_table.hide()
        for i in range(1,self.model.rowCount(0)):
            self.invisible_table.setRowHidden(i,True)
        self.invisible_table.doubleClicked.connect(self.clicked)
        self.invisible_table.clicked.connect(self.clicked)
        self.invisible_table.setMinimumSize(1,1)
        self.invisible_table.resizeColumnsToContents()
        self.invisible_table.resizeRowsToContents()

        # self.box_window.addLayout(self.visible_line)
        self.box_window.addWidget(self.invisible_table)
        self.box_window.addStretch()
        # build central widget and select it
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.centralWidget().setLayout(self.box_window)

        # show window
        self.setGeometry(50,50,1024,768)
        self.setWindowTitle("Test")
        self.show()

    def popup(self, widget):
        print("popup")
        print(widget)
        self.invisible_table.show()

    def clicked(self, qmi):
        print("clicked")
        rowIndex = qmi.row()
        if rowIndex == 0:
            for i in range(1,self.model.rowCount(0)):
                if self.invisible_table.isRowHidden(i):
                    self.invisible_table.setRowHidden(i,False)
                else:
                    self.invisible_table.setRowHidden(i,True)
        else:
            self.selection = data[rowIndex-1]
            print(self.selection)
            col = 0
            for d in self.selection:
                # self.model._data[0][col]=d
                self.model.setData(self.model.index(0,col),d)
                col += 1
            for i in range(1,self.model.rowCount(0)):
                self.invisible_table.setRowHidden(i,True)

def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

答案 3 :(得分:0)

该错误消息非常容易引起误解。尝试将新表添加到DbContext后,出现了相同的错误。 Angular没有给出“ Access-Control-Allow-Origin”错误,但Postman却给出了500 Internal Server错误。尝试调用_userManager.FindByEmailAsync()时,登录方法失败。希望这对其他人有帮助。

答案 4 :(得分:-1)

WebApi项目---&gt;右键单击References ---&gt;在Manage Nuget Packages部分中搜索Core。通过安装

将Microsoft.AspNet.WebApi.Cors添加到项目中

将以下代码添加到项目中App_Start文件夹下的WebApi.Config文件中。

var cors = new EnableCorsAttribute(“”,“”,“*”); config.EnableCors(CORS);

enter image description here

enter image description here

答案 5 :(得分:-4)

万一你懒得理解场景背后的魔法UseCors

    // Summary:
    //     Adds a CORS middleware to your web application pipeline to allow cross domain
    //     requests.
    //
    // Parameters:
    //   app:
    //     The IApplicationBuilder passed to your Configure method.
    //
    //   configurePolicy:
    //     A delegate which can use a policy builder to build a policy.
    //
    // Returns:
    //     The original app parameter

现在您可以安全地使用以下行

    app.UseCors(builder =>
                    builder.WithOrigins("http://localhost:53593")
                        .AllowAnyOrigin()
                        .AllowAnyHeader()
                        .AllowAnyMethod());