Angular 应用程序无法与 asp.net 核心的 SignalR 安排进行协商。由于 CORS 错误

Posted

技术标签:

【中文标题】Angular 应用程序无法与 asp.net 核心的 SignalR 安排进行协商。由于 CORS 错误【英文标题】:Angular app is not being able to negotiate with asp.net core's SignalR arrangement. due to CORS error 【发布时间】:2021-03-11 22:24:01 【问题描述】:

我的 statup.cs 类代码如下所示:

namespace Tenet.eReserve.Api.WebApi

    public class Startup
    
        public IConfiguration Configuration  get; 
        public string SuperSecret  get; set;  
        public Startup(IConfiguration configuration) => Configuration = configuration;


        public void ConfigureServices(IServiceCollection services)
        
            services.AddOptions();
            services.AddSingleton<IConfiguration>(Configuration);
            services.Configure<Settings>(Configuration.GetSection("eReserve"));

            services.AddSwaggerGen(c =>
            
                c.SwaggerDoc("v1", new OpenApiInfo  Title = "eReserve API List", Version = "v1" );
                c.AddSecurityDefinition("Bearer",
                    new OpenApiSecurityScheme
                    
                        Description = "JWT Authorization header using the Bearer scheme.",
                        Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
                        Scheme = "bearer"
                    );

                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                    
                        new OpenApiSecurityScheme
                            Reference = new OpenApiReference
                                Id = "Bearer", //The name of the previously defined security scheme.
                                Type = ReferenceType.SecurityScheme
                            
                        ,new List<string>()
                    
                );

            );

            services.AddControllers().AddJsonOptions(options =>
            
                options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            ).AddNewtonsoftJson(); // to convert string values to int32 and decimal   

            services.AddScoped<IReportService, ReportService>();
            services.AddScoped<ISearchService, SearchService>();


            var sp = services.BuildServiceProvider();

            // This will succeed.
            var settings = sp.GetService<IOptionsSnapshot<Settings>>();

            services.AddApplicationInsightsTelemetry(options =>
            
                options.EnableDebugLogger = false;
            );
            services.AddCors(options =>
            
                options.AddPolicy("CorsPolicy",
                    builder => builder.WithOrigins(settings.Value.SignalROrigniURL) //http://localhost:8090")
                               .AllowAnyHeader()
                           .AllowAnyMethod()
                           .SetIsOriginAllowed((x) => true)
                           .AllowCredentials());

                //  options.AddPolicy("CorsPolicy",
                //                builder => builder.AllowAnyOrigin()
                //.AllowAnyMethod()
                //.AllowAnyHeader());
            );
            services.AddAuthentication(opt =>
            
                opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            )
            .AddJwtBearer(
            );
            services.ConfigureOptions<ConfigureJwtBearerOptions>();
            services.AddHttpContextAccessor();
            services.AddAuthorization(options =>
            
                options.AddPolicy("HospitalCode", policy =>
                    policy.Requirements.Add(new HospitalCodeRequirement()));
            );
            services.AddSingleton<IAuthorizationHandler, FacilityHandler>();
            services.AddSignalR(options =>
            
                options.EnableDetailedErrors = true;
            );
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, TelemetryConfiguration configuration, IOptionsSnapshot<Settings> settings)
        
   
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            
            app.UseAzureAppConfiguration();
            app.UseSwagger();
            // Enable middleware to serve swagger-ui (html, JS, CSS, etc.),
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "eReserve API V1");
                c.RoutePrefix = String.Empty;
            );
            configuration.InstrumentationKey = settings.Value.AppInsightsInstrumentationKey;
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseCors("CorsPolicy");
            app.UseAuthentication(); // this one first
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
           
               endpoints.MapControllers();
               endpoints.MapHub<NotifyClientService>("/lockNotification");
           );
            //app.UseSignalR(routes =>
            //
            //    routes.MapHub<NotifyClientService>("/lockNotification");
            //);
        
    

在角度上,我正在创建这样的连接:

private hubConnection: signalR.HubConnection;

public startConnection = () => 
  this.hubConnection = new signalR.HubConnectionBuilder()
    .withUrl(this.apiBase + 'lockNotification')
    .build();
  this.hubConnection
    .start()
    .then(() => console.log('Connection started'))
    .catch(err => console.log('Error while starting connection: ' + err));


public startListener = () => 
  this.hubConnection.on('LockNotification', (facilityName, lockType, hospitalCode) => 
   //logic

    
  );

这种安排在本地工作,但在部署时会出现 CORS 错误:

CORS 政策已阻止访问来自源“https://abc-ui.com”的“https://abc-api.com/lockNotification/negotiate?negotiateVersion=1”获取:响应预检请求未通过访问控制检查:响应中“Access-Control-Allow-Credentials”标头的值为“”,当请求的凭据模式为“包含”时,该值必须为“真”。 zone.js:1118 POST https://xxx-xxx.com/lockNotification/negotiate?negotiateVersion=1 net::ERR_FAILED

请注意“settings.Value.SignalROrigniURL”返回“https://abc-ui.com”(假网址)

更新

在 ConfigureServices 方法中,我有这样的 Cors:

services.AddCors(options =>

    options.AddPolicy("CorsPolicy",
                  builder => builder.AllowAnyOrigin()
  .AllowAnyMethod()
  .AllowAnyHeader());
);

不确定是否需要上述内容,并且在配置中我遵循了文档。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, TelemetryConfiguration configuration, IOptionsSnapshot<Settings> settings)

   
    if (env.IsDevelopment())
    
        app.UseDeveloperExceptionPage();
    
    app.UseAzureAppConfiguration();
    app.UseSwagger();
    // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
    // specifying the Swagger JSON endpoint.
    app.UseSwaggerUI(c =>
    
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "eReserve API V1");
        c.RoutePrefix = String.Empty;
    );
    configuration.InstrumentationKey = settings.Value.AppInsightsInstrumentationKey;
    app.UseHttpsRedirection();
    app.UseCors(builder =>
    
        builder.WithOrigins(settings.Value.SignalROrigniURL)
            .AllowAnyHeader()
            .WithMethods("GET", "POST")
            .AllowCredentials();
    );
    app.UseRouting();
    app.UseAuthentication(); // this one first
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
   
       endpoints.MapControllers();
       endpoints.MapHub<NotifyClientService>("/lockNotification");
   );
    //app.UseSignalR(routes =>
    //
    //    routes.MapHub<NotifyClientService>("/lockNotification");
    //);

更新 2

CORS 也在部署 api 的位置进行配置。

【问题讨论】:

您是否尝试过 doc 中的配置? docs.microsoft.com/en-us/aspnet/core/signalr/… 我根据文档添加了更新。同样的错误。但是,文档没有说明“ConfigureServices”方法。 您在哪里部署了您的应用程序?您可能需要在部署中配置 CORS,例如 Azure App Service 具有在门户中配置 CORS 的选项。 好的,调查一下 查看更新2.好像配置好了。 【参考方案1】:

您好,您需要完全按照我在类似question 上回答的方式配置 cors:

services.AddCors(options =>

    options.AddPolicy("CorsPolicy", builder => builder.WithOrigins("http://localhost:4200")
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials()
        .SetIsOriginAllowed((host) => true));
);

另外,我建议删除您在 Azure 上的 CORS 配置,以便在服务器上测试此代码的 CORS。在后端正确配置 CORS 后,您将不再需要 Update 2 上的 Azure CORS 配置。

更新

在启动时配置 CORS 的示例:

public void Configure(IApplicationBuilder app, HostConfiguration hostConfiguration, ILogger<Startup> logger)

    app.UseCors("CorsPolicy");


private static void AddCors(IServiceCollection services, HostConfiguration hostConfiguration)

    services.AddCors(options =>
    
        options.AddPolicy("CorsPolicy", builder => builder.WithOrigins(hostConfiguration.CorsPolicy.Origins.ToArray())
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials()
            .SetIsOriginAllowed((host) => hostConfiguration.CorsPolicy.SetIsOriginAllowed));
    );

我在AddAdditionalServices 方法上调用私有AddCors 方法。

【讨论】:

好的,我正在这样做,但 app.UserCors( *** 在 Startup.cs 的“配置”功能中应该是什么样子? 我会更新答案来回答这个问题@RaasMasood 按预期在本地工作。现在部署时将“WithOrigins”设置为 UI 的原始 url 。我已从 azure 门户中删除了允许的来源。会让你知道 我在 HUB url 上收到 404。请不要说我在做“endpoints.MapHub("/lockNotification");”我没有使用 app.UserSignalR*** 我在我的网络 api-base-path/lockNotification 上得到 404,但是当我单击该 URL 时,我看到“没有与该 ID 的连接,这告诉我 URL 有效。 也许您在客户端缺少/.withUrl(this.apiBase + '/lockNotification')

以上是关于Angular 应用程序无法与 asp.net 核心的 SignalR 安排进行协商。由于 CORS 错误的主要内容,如果未能解决你的问题,请参考以下文章

使集成的安全性和 CORS 与 Asp.Net、Angular 6 和 IIS 一起工作

带有 Razor 页面的 ASP .Net Core 与 UI 的 Angular [关闭]

Angular / ASP.Net Web API -- 发布字符串或对象 --> 错误请求

SignalR 授权无法在带有 Identity Server 的 asp.net core angular SPA 中开箱即用

Angular.js $resource 与 ASP.Net webapi?

如何在没有登录表单的情况下将摘要身份验证与 asp.net web api 和 angular js 一起使用?