C# ASP.NET Core 3.1信号器:未调用方法
我有一个应用程序,前端是React,后端是nodejs。我已决定使用Signal重写ASP.NET Core 3.1中的后端。我对ASP.NET Core 3.1和SignalR都是新手,因此我很难确定问题的原因 问题是我从前端调用的hub方法没有被命中。我在几天前遵循了这一点,并设法调用了hub方法,但由于引入了一些特性,例如JWT身份验证和MongoDB,现在没有调用该方法。我不明白为什么 考虑到浏览器中的日志输出,连接似乎成功 我的C# ASP.NET Core 3.1信号器:未调用方法,c#,mongodb,asp.net-core,signalr,auth0,C#,Mongodb,Asp.net Core,Signalr,Auth0,我有一个应用程序,前端是React,后端是nodejs。我已决定使用Signal重写ASP.NET Core 3.1中的后端。我对ASP.NET Core 3.1和SignalR都是新手,因此我很难确定问题的原因 问题是我从前端调用的hub方法没有被命中。我在几天前遵循了这一点,并设法调用了hub方法,但由于引入了一些特性,例如JWT身份验证和MongoDB,现在没有调用该方法。我不明白为什么 考虑到浏览器中的日志输出,连接似乎成功 我的startup.cs如下所示: namespace MpA
startup.cs
如下所示:
namespace MpApp.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder =>
{
builder
.WithOrigins("http://localhost:3000", "http://localhost:3010")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
var domain = $"https://{Configuration["Auth0:Domain"]}/";
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = domain;
options.Audience = Configuration["Auth0:Audience"];
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/chathub")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("read:messages",
policy => policy.Requirements.Add(new HasScopeRequirement("read:messages", domain)));
});
services.AddControllers();
services.AddSignalR();
// Register the scope authorization handler
services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
Debug.WriteLine("===== about to init config =====");
services.Configure<DatabaseSettings>(
Configuration.GetSection(nameof(DatabaseSettings)));
services.AddSingleton<IDatabaseSettings>(sp =>
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
services.AddSingleton<IBaseService, BaseService>();
services.AddSingleton<CollectionService<Profile>>();
services.AddSingleton<CollectionService<User>>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("AllowSpecificOrigin");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
}
namespace MyApp.API.Hubs
{
public class ChatHub : Hub
{
protected CollectionService<Profile> _profileService;
protected CollectionService<User> _userService;
public ChatHub(CollectionService<Profile> profileService, CollectionService<User> userService)
{
// This constructor is being called
_profileService = profileService;
_userService = userService;
}
[Authorize]
public async Task UpdateProfile()
{
// I have put a breakpoint here but it is not being hit
await Clients.All.SendAsync("Test");
}
}
}
useEffect(() => {
(async () => {
if (isAuthenticated) {
const accessToken = await getAccessTokenSilently();
const connection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Debug)
.withUrl('http://localhost:3010/chathub', {accessTokenFactory: () => accessToken})
.build();
await connection.start();
setConnected(true);
}
// eslint-disable-next-line
})()
}, [isAuthenticated]);
useEffect(() => {
(async () => {
if(connected && user) {
// this code is being hit, but the method on the back end is not
await connection?.send('UpdateProfile');
}
})()
}, [connected, user])
React前端似乎在等待正确的连接,以及正确调用方法,但它不工作
来自React提供程序的相关代码如下所示:
namespace MpApp.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder =>
{
builder
.WithOrigins("http://localhost:3000", "http://localhost:3010")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
var domain = $"https://{Configuration["Auth0:Domain"]}/";
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = domain;
options.Audience = Configuration["Auth0:Audience"];
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/chathub")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("read:messages",
policy => policy.Requirements.Add(new HasScopeRequirement("read:messages", domain)));
});
services.AddControllers();
services.AddSignalR();
// Register the scope authorization handler
services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
Debug.WriteLine("===== about to init config =====");
services.Configure<DatabaseSettings>(
Configuration.GetSection(nameof(DatabaseSettings)));
services.AddSingleton<IDatabaseSettings>(sp =>
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
services.AddSingleton<IBaseService, BaseService>();
services.AddSingleton<CollectionService<Profile>>();
services.AddSingleton<CollectionService<User>>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("AllowSpecificOrigin");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
}
namespace MyApp.API.Hubs
{
public class ChatHub : Hub
{
protected CollectionService<Profile> _profileService;
protected CollectionService<User> _userService;
public ChatHub(CollectionService<Profile> profileService, CollectionService<User> userService)
{
// This constructor is being called
_profileService = profileService;
_userService = userService;
}
[Authorize]
public async Task UpdateProfile()
{
// I have put a breakpoint here but it is not being hit
await Clients.All.SendAsync("Test");
}
}
}
useEffect(() => {
(async () => {
if (isAuthenticated) {
const accessToken = await getAccessTokenSilently();
const connection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Debug)
.withUrl('http://localhost:3010/chathub', {accessTokenFactory: () => accessToken})
.build();
await connection.start();
setConnected(true);
}
// eslint-disable-next-line
})()
}, [isAuthenticated]);
useEffect(() => {
(async () => {
if(connected && user) {
// this code is being hit, but the method on the back end is not
await connection?.send('UpdateProfile');
}
})()
}, [connected, user])
有人知道为什么会这样吗?我在调用中尝试了一个小写的方法名,但这没有帮助。将
[Authorize]
属性放在ChatHub
类的顶部。如果启动时未配置任何默认授权方案,则需要包含如下授权方案
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ChatHub: Hub
{
//...
}
var resultString = JsonConvert.SerializeObject(ResultObject, new
JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
Clients.Caller.SendAsync("ClientFunction", resultString );
*注意:当我使用SignalR时,我将DTO转换为JSON字符串并将其传递给服务器,可能需要为具有参数的函数执行此操作。
像这样:
public void FunctionName(string dtoString)
{
var dto = JsonConvert.DeserializeObject<MyObjectDto>(dtoString);
//Do something with my DTO
}
信号器中的令牌是在查询中发送的,所以您需要从查询中读取它们并将它们放在头上
services.AddAuthentication()
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidIssuer = [Issuer Site],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes([YOUR SECRET KEY STRING]))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var path = context.Request.Path;
var accessToken = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chathub"))
{
context.Request.Headers.Add("Authorization", new[] { $"Bearer {accessToken}" });
}
return Task.CompletedTask;
}
};
});
如果你有任何问题,请告诉我。
希望它能起作用 错误消息是什么?您确定已正确获取令牌吗?