【问题标题】:How to Configure Angular 6 with .NET Core 2 to allow CORS from any host?如何使用 .NET Core 2 配置 Angular 6 以允许来自任何主机的 CORS?
【发布时间】:2019-06-02 18:49:21
【问题描述】:

我有一个在 4200 本地运行的简单 Angular 应用程序,我正在测试一个使用 .net 核心在 5000 本地运行的 web api。我的 startup.cs 已正确配置 CORS,以允许我相信的一切。

在 ConfigureServices 部分我有: services.AddCors();

在配置部分我有:

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

但是,当我尝试点击我的 webapi 时,我仍然会在浏览器中看到它。

来自原点“http://localhost:4200”已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:预检请求不允许重定向。

我查看了其他答案,它们似乎都有我尝试过的这种或类似的变体,没有任何变化。不知道我还需要做什么?

【问题讨论】:

    标签: angular asp.net-core cors


    【解决方案1】:

    可能的问题:

    • 在您的startup.cs.Configure() 方法中,app.UseCors() 是否在app.useMVC() 之前?
    • Http 请求 URL 是否包含尾部斜杠 (/)
    • Http 请求是否包含凭据?
    • 浏览器是否接收到来自 API 的 Http 响应?
    • 响应是否包含“Access-Control-Allow-Origin”标头?
    • 当您从 Postman 发送请求时,Http 响应是否包含“Access-Control-Allow-Origin”标头以及包含数据的正文?

    陷阱

    Firefox 需要为您的 API 安装证书才能使用 HTTPS 协议发送 Http 请求。

    使用 Postman 和浏览器开发工具测试您的 API。注意 2 Http 请求。 Http 200 是一个“预测试”,用于查看可用的 CORS 选项。

    • 如果您的 API (.NET) 抛出 HTTP 500 (Internal Server Error),它将返回开发人员异常页面,并且 Postman 将显示 “no ‘Access-Control-Allow-Origin’ header is present on the requested resource” 消息 - 这是误导
    • 这种情况下的实际问题是开发者异常页面无法从服务器返回到运行在不同 Origin 上的客户端(浏览器)。

    我想恭敬地指出以下几点:

    • CORS 规范指出,如果 Access-Control-Allow-Credentials 标头存在,则将来源设置为“*”(所有来源)无效。
    • AllowAnyOrigin() 不建议在生产环境中使用,除非您打算允许任何人使用您的 API 并且您不会实施凭据。
    • 在使用多个 CORS 策略时,您不需要在控制器级别使用 EnableCors 属性配置 CORS。
    • 使用 ASP.NET Core 配置多个 CORS 策略很容易(请参阅下面的教程

    以下文章值得回顾:

    ASP.NET Core 2.2 does not permit allowing credentials with AllowAnyOrigin()

    Enable Cross-Origin Requests (CORS) in ASP.NET Core

    关键点(tldr;):

    • CORS 中间件必须位于应用配置中任何已定义的端点之前。
    • 指定的 URL 必须没有尾部斜杠(/)
    • 如果浏览器发送凭据但响应不包含 有效的 Access-Control-Allow-Credentials 标头,浏览器没有 将响应暴露给应用,跨域请求失败。
    • CORS 规范还规定将源设置为“*”(所有 origins) 如果 Access-Control-Allow-Credentials 标头是无效的 展示。
    • 如果浏览器支持 CORS,它会自动为跨域请求设置这些标头。
    • 如果响应不包含 Access-Control-Allow-Origin 标头,则跨域请求失败。

    CORS 教程:(2)Angular 客户端 + ASP.NET Core

    1. 创建 Visual Studio 解决方案

      md c:\s\a cd c:\s\a c:\s\a>dotnet new sln -n 解决方案名称
    2. 创建 ASP.NET Core 项目

      c:\s\a>md s c:\s\a>cd s c:\s\a\s>dotnet new webapi -o api -n api
    3. API 启动设置和 CORS 配置

    launchSettings.json

    将开发配置文件克隆到暂存配置文件

    {
    "$schema": "http://json.schemastore.org/launchsettings.json",
    "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iis": {
        "applicationUrl": "http://localhost:myIISApiPortNumber",
        "sslPort": myIISApiSSLPortNumber
    },
    "iisExpress": {
        "applicationUrl": "http://localhost:myIISExpressApiPortNumber",
        "sslPort": myIISExpressApiSSLPortNumber
    }
    },
    "profiles": {
    "Development (IIS Express)": {
        "commandName": "IISExpress",
        "launchBrowser": true,
        "launchUrl": "api/values",
        "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
        }
    },
    "Staging (IIS Express)": {
        "commandName": "IISExpress",
        "launchBrowser": true,
        "launchUrl": "api/values",
        "applicationUrl": "https://localhost:myIISExpressApiSSLPortNumber;http://localhost:myIISExpressApiPortNumber",
        "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Staging"
        }
    },
    "Production (IIS)": {
        "commandName": "IIS",
        "launchBrowser": true,
        "launchUrl": "api/values",
        "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
        },
        "applicationUrl": "https:localhost:myIISApiSSLPortNumber;http://localhost:myIISApiPortNumber"
    }
    }
    }
    

    startup.cs

    添加 CORS 配置

    public class Startup
    {
        public IConfiguration Configuration { get; }
    
        public IServiceCollection _services { get; private set; }
    
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public void ConfigureServices(IServiceCollection services)
        {
            _services = services;
            RegisterCorsPolicies();
            services.AddMvc()
                    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseCors("DevelopmentCorsPolicy");
                app.UseDeveloperExceptionPage();
            }
            else if (env.IsStaging())
            {
                app.UseCors("StagingCorsPolicy");
            }
            else
            {
                app.UseCors("ProductionCorsPolicy");
                app.UseHsts();
            }
    
            app.UseHttpsRedirection();
            app.UseMvc(); // CORS middleware must precede any defined endpoints
        }
    
        private void RegisterCorsPolicies()
        {
            string[] localHostOrigins = new string[] {
            "http://localhost:4200", "http://localhost:3200"};
    
            string[] stagingHostOrigins= new string[] {
            "http://localhost:4200"};
    
            string[] productionHostOrigins = new string[] {
            "http://yourdomain.net", "http://www.yourdomain.net",
            "https://yourdomain.net", "https://www.yourdomain.net"};
    
            _services.AddCors(options =>    // CORS middleware must precede any defined endpoints
            {
                options.AddPolicy("DevelopmentCorsPolicy", builder =>
                {
                    builder.WithOrigins(localHostOrigins)
                            .AllowAnyHeader().AllowAnyMethod();
                });
                options.AddPolicy("StagingCorsPolicy", builder =>
                {
                    builder.WithOrigins(stagingHostOrigins)
                            .AllowAnyHeader().AllowAnyMethod();
                });
                options.AddPolicy("ProductionCorsPolicy", builder =>
                {
                    builder.WithOrigins(productionHostOrigins)
                            .AllowAnyHeader().AllowAnyMethod();
                });
            //options.AddPolicy("AllowAllOrigins",
            //    builder =>
            //    {
            // WARNING: ASP.NET Core 2.2 does not permit allowing credentials with AllowAnyOrigin() 
            // cref: https://docs.microsoft.com/en-us/aspnet/core/migration/21-to-22?view=aspnetcore-2.2&tabs=visual-studio
                //        builder.AllowAnyOrigin()
                //               .AllowAnyHeader().AllowAnyMethod();
                //    });
                //options.AddPolicy("AllowSpecificMethods",
                //    builder =>
                //    {
                //        builder.WithOrigins(productionHostOrigins)
                //               .WithMethods("GET", "POST", "HEAD");
                //    });
                //options.AddPolicy("AllowSpecificHeaders",
                //    builder =>
                //    {
                //        builder.WithOrigins(productionHostOrigins)
                //               .WithHeaders("accept", "content-type", "origin", "x-custom-header");
                //    });
                //options.AddPolicy("ExposeResponseHeaders",
                //    builder =>
                //    {
                //        builder.WithOrigins(productionHostOrigins)
                //               .WithExposedHeaders("x-custom-header");
                //    });
                //options.AddPolicy("AllowCredentials",
                // WARNING: ASP.NET Core 2.2 does not permit allowing credentials with AllowAnyOrigin() cref: https://docs.microsoft.com/en-us/aspnet/core/migration/21-to-22?view=aspnetcore-2.2&tabs=visual-studio
                //    builder =>
                //    {
                //        builder.WithOrigins(productionHostOrigins)
                //               .AllowCredentials();
                //    });
                //options.AddPolicy("SetPreflightExpiration",
                //    builder =>
                //    {
                //        builder.WithOrigins(productionHostOrigins)
                //               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
                //    });
            });
        }
    }
    
    1. 使用 开发 (IIS Express) 配置文件启动调试 API

    ValuesController.Get()上设置断点

    1. 使用 Postman 测试 API:

      https://localhost:myApiPortNumber/api/values

      • Access-Control-Allow-Origin 标头和值应作为响应
    2. 创建 Angular 应用程序

      c:\s\a\s>ng new Spa1 --routing(会自动创建Spa文件夹)
    3. 启动 Spa1 应用程序

      c:\s\a\s>cd Spa1 c:\s\a\s\Spa1>Ng 发球
    4. 浏览到http://localhost:4200/

      • Spa1 应该成功启动
    5. 在 Spa1 中实现 COR

    app.module.ts

    • 导入 HttpClientModule
    import { HttpClientModule } from '@angular/common/http';
    
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        HttpClientModule,
        BrowserModule,
        AppRoutingModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    app.component.ts

    • 导入 HttpClient

    • 使用 CORS 请求添加 getValues() 方法

    import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    
    export class AppComponent implements OnInit {
      title = 'Spa1';
      values: any;
      apiUrl: string = environment.apiUrl;
      valuesUrl = this.apiUrl + "values";
    
      constructor(private http: HttpClient) { }
    
      ngOnInit() {
        this.getValues();
      }
      getValues() {
        this.http.get(this.valuesUrl).subscribe(response => {
          this.values = response;
      }, error => {
      console.log(error);
    });
    }
    }
    

    app.component.html

    <div style="text-align:center">
      <h1>
        Welcome to {{ title }}!
      </h1>
    </div>
    <h2>Values</h2>
    <p *ngFor="let value of values">
      {{value}}
    </p>
    <router-outlet></router-outlet>
    

    environment.ts

    export const environment = {
      production: false,
      apiUrl: 'https://localhost:myApiPortNumber/api/'
    };
    
    1. 启动 Spa1 应用程序

      c:\s\a\s\Spa1>Ng 发球
    2. 浏览到http://localhost:4200/

      • Chrome 和 Edge:现在应该可以成功发出 COR 请求
      • Safari:我没有测试过
      • Firefox:由于证书不受信任,可能会阻止请求。

    修复 Firefox 阻塞的一种方法:

    火狐 |选项 |隐私与安全 |安全 |证书 | [查看证书]:

    证书管理器 | [添加例外]:

       add localhost
    

    CORS 测试

    1. 克隆 Spa1

      c:\s\a\s>xcopy /s /i Spa1 Spa2
    2. 重构 Spa2 的标题

    app.component.ts

    export class AppComponent implements OnInit {
      title = 'Spa2';
    }
    
    1. 端口 3200

      上启动 Spa2 应用程序 c:\s\a\s\Spa2>ng 服务 --port 3200
    2. 浏览到http://localhost:3200/

      • 值数组应呈现在网页上
    3. 使用开发 (IIS Express) 配置文件停止调试 API

    4. 使用 Staging (IIS Express) 配置文件开始调试 API

    5. 浏览到http://localhost:4200/

      • 值数组应该在网页上呈现
    6. 浏览到http://localhost:3200/

      • 值数组不应呈现在网页上。
    7. 使用开发者工具检查 Http 响应:

      • Access-Control-Allow-Origin 标题和值不应作为响应

    【讨论】:

    • 我已经为此浪费了一整天的时间。感谢那。解释得很好!
    【解决方案2】:

    在方法ConfigureServices中添加如下代码sn-p。编辑此项以仅允许自定义标题。

    // Add service and create Policy with options
            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials()
                    );
            });
    

    在配置方法中添加以下内容

    app.UseCors("CorsPolicy");
    

    将“EnableCors”属性添加到控制器。

    [EnableCors("CorsPolicy")]
    

    【讨论】:

    • 谢谢!我没有意识到我还需要将 EnableCors 属性添加到控制器。我做到了,一切正常。
    • 如果全局启用,则无需将 EnableCors 属性添加到控制器。
    • @randydaddis 如果我们有多个政策,这是必需的,如果我错了,请纠正我。
    • @KrishnaMohan 我能够向未配置“EnableCors”属性的控制器(从 ControllerBase 派生)发出 COR 请求。请参阅下面我的答案中的 Startup.cs 类,了解我全局配置 CORS 策略的方式。
    • 你是救命稻草
    【解决方案3】:

    在服务中注册 cors

    services.AddCors(options =>
                {
                    options.AddPolicy("CorsPolicy",
                        builder => builder
                        .SetIsOriginAllowed((host) => true)
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
                });
    

    添加 cors 中间件

    app.UseCors("CorsPolicy");
    

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,但我的情况是 windowsauthentification

      @RandyDaddis 为我指明了正确的方向。 如果设置了身份验证,则不能使用“*”。

      但这并不能解决漏洞问题(对我来说,'*' 不是一个好的选择)。

      我不得不从 windowsAuthentication only 更改为 混合模式(其中还启用了 anonymousAuthentication),因为 preflight 没有发送凭据。

      这已经是asp 讨论的问题。

      重要的是您将其添加到 ConfigureServices 函数中的 startup.cs 中,以确保您的所有控制器仍在授权策略下:

       ...
       services.AddAuthentication(IISDefaults.AuthenticationScheme);
       services.AddMvc(options =>
              {
                  var policy = new AuthorizationPolicyBuilder()
                      .RequireAuthenticatedUser()
                      .Build();
                  options.Filters.Add(new AuthorizeFilter(policy));
              })...
      

      【讨论】:

        猜你喜欢
        • 2018-07-05
        • 1970-01-01
        • 1970-01-01
        • 2019-02-01
        • 2020-11-18
        • 2022-01-17
        • 2019-03-03
        • 2020-09-07
        • 2019-07-02
        相关资源
        最近更新 更多