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
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 returnundefined
. If you want the connection ID use 3.1+ and accessconnection.connectionId
. From what you've shown everything is working fine. Are you actually triggering the client method from anywhere? Did you triggerAdicionar
orNotificar
? – Brennan