简体   繁体   中英

Angular app is not being able to negotiate with asp.net core's SignalR arrangement. due to CORS error

My statup.cs class code looks like this:

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");
            //});
        }
    }
}

And in the angular I am creating a connection like this:

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

    }
  });
}

This arrangement works locally but when deployed it give CORS error:

Access to fetch at 'https://abc-api.com/lockNotification/negotiate?negotiateVersion=1' from origin 'https://abc-ui.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. zone.js:1118 POST https://xxx-xxx.com/lockNotification/negotiate?negotiateVersion=1 net::ERR_FAILED

Please note that "settings.Value.SignalROrigniURL" return "https://abc-ui.com" (fake url)

UPDATE

In the ConfigureServices method i have Cors like this:

services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy",
                  builder => builder.AllowAnyOrigin()
  .AllowAnyMethod()
  .AllowAnyHeader());
});

Not sure if above is required and in Configure I followed the documentation.

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");
    //});
}

Update 2

CORS is also configured where the apis are deployed.

在此处输入图像描述

Hello you need to configure cors exactly like I answered on this similar question :

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

Also I would recommend remove the CORS configuration that you have on Azure to test this code for CORS on the server. After configuring correctly the CORS on the backend you would not need the Azure CORS configuration that you have on Update 2 .

Update

Example on configuring CORS on startup:

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));
    });
}

I call the private AddCors method on the AddAdditionalServices method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM