如何使用.NET Core 2配置Angular 6以允许来自任何主机的CORS?

时间:2019-01-08 05:26:08

标签: angular asp.net-core cors

我有一个在4200本地运行的简单Angular App,我正在测试使用.net core在5000本地运行的Web API。我的startup.cs的CORS配置正确,可以允许我相信的一切。

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

在“配置”部分中,我有:

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

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

  

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

我查看了其他答案,它们似乎都具有我尝试没有改变的这种或类似变化。不确定我还需要做什么?

4 个答案:

答案 0 :(得分:5)

可能的问题:

  • 在您的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标头,则将原点设置为“ *”(所有原点)无效。
  • 除非您打算允许任何人使用您的API并且不会实现凭据,否则不建议在产品中使用
  • AllowAnyOrigin()
  • 使用多个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规范还指出将原点设置为“ *”(所有 如果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 solutionName
    
  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 (will automatically create Spa folder)
    
  3. 启动Spa1应用程序

    c:\s\a\s>cd Spa1
    c:\s\a\s\Spa1>Ng serve
    
  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 serve
    
  2. 浏览到http://localhost:4200/

    • Chrome和Edge:应该立即成功发出COR请求
    • Safari:我尚未测试
    • Firefox:由于证书不受信任,可能会阻止请求。

补救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 serve --port 3200
    
  2. 浏览到http://localhost:3200/

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

  4. 使用登台(IIS Express)配置文件

  5. 开始调试API
  6. 浏览到http://localhost:4200/

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

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

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

答案 1 :(得分:2)

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

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

在Configure方法中添加以下内容

app.UseCors("CorsPolicy");

向控制器添加“ EnableCors”属性。

[EnableCors("CorsPolicy")]

答案 2 :(得分:1)

在服务中注册cors

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

添加cors中间件

app.UseCors("CorsPolicy");

答案 3 :(得分:0)

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

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

但这不能解决孔问题(对我来说'*'不是一个好选择)。

由于preflight未发送凭据,我不得不从仅WindowsAuthentication 更改为混合模式(其中还启用了匿名身份验证)。

这已经是asp讨论的问题。

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

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