0
votes

I am migrating an ASP.NET Web API 2 service to ASP.NET Core 3.1 Web API, and I already resolved all dependencies and problems, but I'm not getting the SignalR.

Strange things:

  • I can connect but I'm receiving any message: chrome console connection
  • The id about this connection is always 0(in asp.net framework, was always an unique code)
  • I put a custom authorize in the hub:

Code:

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

With a break point, but the breakpoint is not getting hit: print authorize with breakpoint

My startup configuration:

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

How I am sending the message: broadcast message

And how I am listening in 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());
});

Thank you very much

1
connection.id isn't a public property you should be accessing. In the 2.1 version of the client this will be an internal number tracking invocation IDs. In 3.1+ this will return undefined. If you want the connection ID use 3.1+ and access connection.connectionId. From what you've shown everything is working fine. Are you actually triggering the client method from anywhere? Did you trigger Adicionar or Notificar?Brennan
HI @Brennan Yes, and the strange thing is that I put a breakpoint in method "Adicionar" to see what is in _hubnotification and I see that the count of connections in _hubNotifications.Clients.All._lifetimeManager._connections is zero. But, I am connecting, otherwise I would receiving 404, or any error 🤔rafitas

1 Answers

0
votes

If I try send from client o server:

connection.connection.start().then(function () {

      console.log('Now connected, connection ID=' + connection.id)
      console.log(connection);

      connection.invoke('Notificar').then(function (connectionId) {
        console.log('invoke notificar');
        console.log(connectionId);
      })

    }).catch(function (err) {
      return console.error(err.toString());
    });

I got: Print of chrome console