【发布时间】:2020-11-13 18:15:07
【问题描述】:
我正在将 ASP.NET Web API 2 服务迁移到 ASP.NET Core 3.1 Web API,并且我已经解决了所有依赖项和问题,但我没有得到 SignalR。
奇怪的事情:
- 我可以连接,但我收到任何消息: chrome console connection
- 此连接的 id 始终为 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á");
}
}
}
有断点,但断点未命中:print authorize with breakpoint
我的启动配置:
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;
}
}
}
我如何发送消息:broadcast message
以及我在 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());
});
非常感谢
【问题讨论】:
-
connection.id不是您应该访问的公共财产。在 2.1 版本的客户端中,这将是一个内部号码跟踪调用 ID。在 3.1+ 中,这将返回undefined。如果您想要连接 ID,请使用 3.1+ 并访问connection.connectionId。从你所展示的一切都很好。您实际上是从任何地方触发客户端方法吗?你触发了Adicionar还是Notificar? -
HI @Brennan 是的,奇怪的是我在方法“Adicionar”中放置了一个断点来查看 _hubnotification 中的内容,我看到 _hubNotifications.Clients.All._lifetimeManager 中的连接数._connections 为零。但是,我正在连接,否则我会收到 404 或任何错误????
标签: asp.net-core signalr signalr-hub signalr.client asp.net-core-signalr