ASP.NET Ядро 3.1 SignalR подключается, но не работает

#asp.net-core #signalr #signalr-hub #signalr.client #asp.net-core-signalr

#asp.net-ядро #signalr #signalr-концентратор #signalr.client #asp.net-core-signalr

Вопрос:

Я переношу ASP.NET Веб-API 2 сервис для ASP.NET Core 3.1 Web API, и я уже разрешил все зависимости и проблемы, но я не получаю сигнал.

Странные вещи:

  • Я могу подключиться, но получаю какое-либо сообщение: подключение к консоли Chrome
  • Идентификатор этого соединения всегда равен 0 (в asp.net фреймворк, всегда был уникальным кодом)
  • Я ввел пользовательскую авторизацию в концентратор:

Код:

 using Flow2.Api.Security;
using Microsoft.AspNetCore.SignalR;

namespace Flow2.Api.Hubs
{
    [AuthorizeSignalR]
    public class HubNotification : Hub
    {
        public void Notificar()
        {
            Clients.All.SendAsync("broadcastMessage", "oi", "olá");
        }
    }
}
  

С точкой останова, но точка останова не попадает: печать авторизации с точкой останова

Моя конфигурация запуска:

 using System;
using System.Globalization;
using System.IO.Compression;
using System.Runtime.InteropServices;
using DataTables.AspNet.AspNetCore;
using Flow2.Api.Hangfire;
using Flow2.Api.Helpers;
using Flow2.Api.Hubs;
using Flow2.Api.JsonConverters;
using Flow2.Api.Security;
using Flow2.Api.Security.SignalR;
using Flow2.Mappers;
using Flow2.SharedKernel.Config;
using Hangfire;
using Hangfire.SqlServer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Flow2.CrossCutting.Startup;
using Flow2.Domain.Interfaces;
using Flow2.Domain.Outros.Arquivo.Interfaces;
using Flow2.SharedKernel;
using Flow2.SharedKernel.Events;
using Flow2.SharedKernel.Helpers;
using Flow2.SharedKernel.Helpers.Email;
using Hangfire.MemoryStorage;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.SwaggerUI;

namespace Flow2.Api
{
    using System.Linq;
    using Microsoft.OpenApi.Models;
    using Swagger.Filters;
    using Swagger;

    public class Startup
    {
        private string _policyCors { get; } = "MyPolicy";
        private readonly IConfiguration _configuration;
        private readonly IWebHostEnvironment _webHostEnvironment;

        public Startup(IConfiguration configuration, IWebHostEnvironment appEnv)
        {
            _configuration = configuration;
            _webHostEnvironment = appEnv;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services
                .ConfigurarControllers()
                .ConfigurarCors(_policyCors)
                .AdicionarSwagger(_webHostEnvironment)
                .AdicionarInjecaoDependencia(_configuration)
                .AdicionarHangfire(_configuration, _webHostEnvironment)
                //.ConfigurarCompressaoResposta()
                .AdicionarAutenticacao(_configuration, _webHostEnvironment)
                .AdicionarMailService(_configuration)
                .AddSignalR(options => { options.EnableDetailedErrors = true; });

            //services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
            services.RegisterDataTables();

            if (_webHostEnvironment.IsProduction())
                services.AddApplicationInsightsTelemetry();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IRecurringJobManager recurringJobManager, IServiceProvider serviceProvider)
        {
            app.UseCors(_policyCors);

            var scope = serviceProvider.CreateScope();
            DomainEvent.Container = new DomainEventsContainer(scope);

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                TelemetryConfiguration.Active.DisableTelemetry = true;
                TelemetryDebugWriter.IsTracingDisabled = true;
            }
            else
            {
                app.UseHsts();
                app.UseHttpsRedirection();
                ConfigurarHangfire(app, recurringJobManager, serviceProvider);
            }

            app.UseStaticFiles();
            app.UseRouting();
            //app.UseResponseCompression();
            //app.UseMiddleware<WebSocketsMiddleware>();
            app.UseAuthentication();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<HubNotification>("/hubnotifications");
            });

            app.UseSwagger(c =>
            {
                c.RouteTemplate = "docs/{documentname}/swagger.json";
            });
            app.UseSwaggerUI(c =>
            {
                c.InjectJavascript("/custom.js");
                if (_webHostEnvironment.IsDevelopment())
                    c.SwaggerEndpoint("/docs/local/swagger.json", "Flow local");

                c.SwaggerEndpoint("/docs/v1/swagger.json", "Flow v1");
                c.SwaggerEndpoint("/docs/v2/swagger.json", "Flow v2");

                c.InjectStylesheet("/custom.css");
                c.RoutePrefix = "docs";
                c.DisplayRequestDuration();
                c.DocExpansion(DocExpansion.List);
                c.DefaultModelRendering(ModelRendering.Example);
            });

            CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-BR");

            AutomapperConfig.RegisterMappings();

            DelegateCompilerConfig.ConfigurarDelegateCompiler();
        }

        private void ConfigurarHangfire(IApplicationBuilder app, IRecurringJobManager recurringJobManager, IServiceProvider serviceProvider)
        {
            app.UseHangfireDashboard("/hangfire", new DashboardOptions
            {
                Authorization = new[] { new HangfireAuthorizationFilter() },
            });

            var azureManagerBackupService = serviceProvider.GetService<IAzureManageBackupService>();
            var atualizadorBancos = serviceProvider.GetService<IAtualizadorBancosDeDadosService>();

            var timeZoneInfo = TimezoneHelper.ObterTimezoneBrasil();

            recurringJobManager.AddOrUpdate(
                "RemoverNotificacoesAntigas",
                () => atualizadorBancos.RemoverNotificacoesAntigas(30),
                "0 1 * * *",
                timeZoneInfo
            );
            recurringJobManager.AddOrUpdate(
                "RemoverBackupsAzure",
                () => azureManagerBackupService.RemoverPageBlobsAntigos(15),
                "0 2 * * *",
                timeZoneInfo
            );
            recurringJobManager.AddOrUpdate(
                "DispararEmailEmpresasInativas",
                () => atualizadorBancos.DispararEmailDeAvisoEmpresasInativas(7),
                "0 3 * * *",
                timeZoneInfo
            );

            recurringJobManager.AddOrUpdate(
                "DispararEmailAvisoCursos",
                () => atualizadorBancos.DispararEmailDeAvisoCursosFuncionarios(),
                "0 6 * * *",
                timeZoneInfo
            );
        }
    }

    public static class StartupConfiguracoes
    {
        public static IServiceCollection AdicionarSwagger(this IServiceCollection services, IWebHostEnvironment hostEnvironment)
        {
            services.AddSwaggerGen(c =>
            {
                if (hostEnvironment.IsDevelopment())
                    c.SwaggerDoc("local", new OpenApiInfo() {Title = "Documentação Flow", Version = "local"});

                c.SwaggerDoc("v1", new OpenApiInfo() {Title = "Documentação Flow", Version = "v1"});
                c.SwaggerDoc("v2", new OpenApiInfo() {Title = "Documentação Flow", Version = "v2"});
                c.IgnoreObsoleteActions();
                c.IgnoreObsoleteProperties();
                c.IncludeXmlComments(FilesComentsSwagger.API);
                c.IncludeXmlComments(FilesComentsSwagger.Domain);
                c.IncludeXmlComments(FilesComentsSwagger.SharedKernel);
                c.OperationFilter<SwaggerImplementationNotesOperationFilter>();
                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
                    In = ParameterLocation.Header,
                    Description = "Please insert JWT with Bearer into field",
                    Name = "Authorization",
                    Type = SecuritySchemeType.ApiKey,
                    BearerFormat = "Bearer {access_token}",
                    Scheme = "Bearer"
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                        },
                        new string[] { }
                    }
                });
                c.OperationFilter<AuthResponsesOperationFilter>();
                c.DocInclusionPredicate((targetApiVersion, apiDesc) =>
                {
                    return targetApiVersion == "local" || apiDesc.RelativePath.Contains(targetApiVersion);
                });
            });

            return services;
        }

        public static IServiceCollection ConfigurarControllers(this IServiceCollection services)
        {
            services.AddControllers().AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.Formatting = Formatting.None;
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
                options.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
                options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
                options.SerializerSettings.Converters = new JsonConverter[] {new ValorCampoPersonalizadoConverter()};
            });
            services.AddControllers();

            return services;
        }

        public static IServiceCollection ConfigurarCors(this IServiceCollection services, string policy)
        {
            services.AddCors(o => o.AddPolicy(policy, builder =>
            {
                builder
                    .SetIsOriginAllowed(isOriginAllowed: _ => true)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials()
                    .WithExposedHeaders("x-nome-arquivo", "x-notificacoes");
            }));

            return services;
        }

        public static IServiceCollection AdicionarHangfire(this IServiceCollection services, IConfiguration iconfiguration, IWebHostEnvironment hostEnvironment)
        {

            if (hostEnvironment.IsProduction())
            {
                services.AddHangfire(config => config
                    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                    .UseSimpleAssemblyNameTypeSerializer()
                    .UseRecommendedSerializerSettings()
                    .UseSqlServerStorage(iconfiguration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
                    {
                        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                        QueuePollInterval = TimeSpan.Zero,
                        UseRecommendedIsolationLevel = true,
                        DisableGlobalLocks = true,
                    }));
            }
            else
            {
                services.AddHangfire(c =>
                    c.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                        .UseMemoryStorage());
            }

            services.AddHangfireServer();

            return services;
        }

        public static IServiceCollection ConfigurarCompressaoResposta(this IServiceCollection services)
        {
            services.Configure<GzipCompressionProviderOptions>(x =>
            {
                x.Level = CompressionLevel.Fastest;
            });

            services.AddResponseCompression(x =>
            {
                x.Providers.Add<GzipCompressionProvider>();
            });

            services.AddResponseCaching();

            return services;
        }

        public static IServiceCollection AdicionarAutenticacao(this IServiceCollection services, IConfiguration config, IWebHostEnvironment env)
        {
            var apiJwtToken = new JwtTokenConfig();

            config.GetSection(nameof(JwtTokenConfig)).Bind(apiJwtToken);

            services.AddSingleton(apiJwtToken);

            services.AddAuthentication(o =>
            {
                o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;

                o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(x =>
            {
                x.TokenValidationParameters = apiJwtToken.TokenValidationParameters;
                x.RequireHttpsMetadata = !env.IsDevelopment();
            });

            return services;
        }

        public static IServiceCollection AdicionarMailService(this IServiceCollection services, IConfiguration config)
        {
            var mailConfig = new MailConfig();

            config.GetSection(nameof(MailConfig)).Bind(mailConfig);

            services.AddSingleton(mailConfig);
            services.AddSingleton<MailMessageSender>();

            return services;
        }
    }
}
  

Как я отправляю сообщение: широковещательное сообщение

И как я слушаю в Angular:

 const options = {
  accessTokenFactory: () => this.sharedService.token
};

let connection = new signalR.HubConnectionBuilder()
    //.withUrl(environment.serviceBaseSignalR   "hubnotifications", options)
    .withUrl("http://localhost:25580/hubnotifications", options)
    .configureLogging(signalR.LogLevel.Information)
    .build();
console.log(connection);

connection.on('broadcastMessage', function (name, message) {
  console.log('message received:');
  console.log(name);
  console.log(message);
});

connection.connection.start().then(function () {
  console.log('Now connected, connection ID='   connection.id)
  console.log(connection);
}).catch(function (err) {
  return console.error(err.toString());
});
  

Комментарии:

1. connection.id это не общедоступное свойство, к которому вы должны обращаться. В версии 2.1 клиента это будет внутренний номер, отслеживающий идентификаторы вызова. В 3.1 это вернется undefined . Если вам нужен идентификатор соединения, используйте 3.1 и access connection.connectionId . Из того, что вы показали, все работает нормально. Вы на самом деле запускаете клиентский метод из любого места? Вы запустили Adicionar или Notificar ?

2. ПРИВЕТ @Brennan Да, и самое странное, что я поставил точку останова в методе «Adicionar», чтобы посмотреть, что находится в _hubnotification, и я вижу, что количество подключений в _hubNotifications. Clients.All._lifetimeManager. _connections равно нулю. Но я подключаюсь, иначе я получу 404 или какую-либо ошибку 🤔

3. @Brennan — вы получили какое-либо разрешение для этого? любое направление, предложение было бы здорово….