ASP.NET Core SignalR+Blazor中WebSocket连接失败问题排查求助
SignalR WebSocket连接失败排查与解决方案
问题描述
使用ASP.NET Core API结合SignalR与Blazor客户端实现通知系统,已配置NotificationHub和自定义用户ID提供者管理用户连接,但启动应用时始终出现WebSocket连接失败错误:
WebSocket connection to 'wss://localhost:63225/notificationHub?id=LDfet**&access_token=ey**' failed:
已验证access_token生成正确且用户已完成认证,同时在NotificationHub中实现OnConnectedAsync方法记录用户标识符,Blazor客户端通过IAccessTokenProvider正确获取令牌。
相关代码片段
NotificationHub
[Authorize] public class NotificationHub : Hub { public override Task OnConnectedAsync() { Debug.WriteLine(Context.UserIdentifier); return base.OnConnectedAsync(); } }
CustomUserIdProvider
public class CustomUserIdProvider : IUserIdProvider { public string? GetUserId(HubConnectionContext connection) { return connection.User?.Identity?.Name; } }
添加请求并发送通知的业务代码
public async Task<int> AddEvolutionDemand(EvolutionDto evolutionToAddDto) { var EvolutionDemand = new Evolution(); try { EvolutionDemand = new() { Service_ID = evolutionToAddDto.Service_ID, User_ID = evolutionToAddDto.User_ID, User_Name = evolutionToAddDto.User_Name, Doc_ID = evolutionToAddDto.Doc_ID, Demande_Creation_Date = evolutionToAddDto.Demande_Creation_Date, Demand_Type = evolutionToAddDto.Demand_Type, Demand_Title = evolutionToAddDto.Demand_Title, Demande_Comment = evolutionToAddDto.Demande_Comment, STATUS = evolutionToAddDto.STATUS, Demand_Assigned_ID = evolutionToAddDto.Demand_Assigned_ID, Demand_update_date = evolutionToAddDto.Demand_update_date, Demand_Update_Comment = evolutionToAddDto.Demand_Update_Comment, }; await _kmDbContext.Evolution.AddAsync(EvolutionDemand); await _kmDbContext.SaveChangesAsync(); var UserToNotify = await UserRepository.GetUser(evolutionToAddDto.Demand_Assigned_ID); await HubConntextion.Clients.User(UserToNotify.User_Login).SendAsync("notification", $"{DateTime.Now}"); } catch (Exception ex) { _logger.Error(ex, ErrorMessage, ex.Message); } return EvolutionDemand.Demand_ID; }
Blazor客户端代码
@code { private HubConnection? hubConnection; public int pendingDemandCount { get; set; } = 0; List<string> notifications = []; [Inject] IAccessTokenProvider AccessTokenProvider { get; set; } = default!; protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); var authState = await AuthenticationStateTask; if (authState.User.Identity != null && authState.User.Identity.IsAuthenticated) { await connectToNotificationHub(); await RefreshPendingDemands(); } } public async Task connectToNotificationHub() { try { if (hubConnection is not null) return; var notificationUrl = "notificationHub"; hubConnection = new HubConnectionBuilder() .WithUrl($"https://localhost:63225/{notificationUrl}", options => { options.AccessTokenProvider = () => GetAccessTokenAsync(); }) .Build(); hubConnection.On<string>("notification", async (message) => { notifications.Add(message); await RefreshPendingDemands(); await InvokeAsync(StateHasChanged); }); await hubConnection.StartAsync(); } catch (Exception ex) { Console.WriteLine($"Error connecting to SignalR hub: {ex.Message}"); Console.WriteLine($"Stack trace: {ex.StackTrace}"); } } private async Task<string?> GetAccessTokenAsync() { var tokenResult = await AccessTokenProvider.RequestAccessToken(); if (tokenResult != null && tokenResult.TryGetToken(out AccessToken accessToken)) { return accessToken.Value; } return null; } private async Task RefreshPendingDemands() { var pendingDemands = await DocumentService.GetPendingDemandsForUser(); pendingDemandCount = pendingDemands?.Count ?? 0; } public async ValueTask DisposeAsync() { if (hubConnection is not null) { await hubConnection.DisposeAsync(); hubConnection = null; } } }
排查与解决步骤
1. 检查服务端SignalR路由配置
确认在Program.cs中正确映射SignalR hub,且顺序在认证中间件之后:
// 先添加认证中间件 app.UseAuthentication(); app.UseAuthorization(); // 再映射SignalR hub app.MapHub<NotificationHub>("/notificationHub");
若路由映射在认证中间件之前,会导致[Authorize]特性无法正确验证令牌,直接拒绝连接。
2. 验证令牌权限与声明
- 用JWT解析工具查看access_token,确认包含
Identity.Name对应的声明(如name或unique_name),因为自定义CustomUserIdProvider依赖该值。 - 检查令牌过期时间,避免因令牌过期导致连接失败。
- 若使用自定义授权策略,确认令牌包含对应角色或声明。
3. 检查CORS配置
Blazor客户端与API跨域时,需确保CORS策略允许SignalR的WebSocket请求:
builder.Services.AddCors(options => { options.AddPolicy("SignalRCors", policy => { policy.WithOrigins("https://your-blazor-client-url") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() // 必须启用,SignalR会携带凭证 .WithExposedHeaders("WWW-Authenticate"); }); }); app.UseCors("SignalRCors");
4. 开启服务端详细日志
在appsettings.json中配置日志级别,捕获连接过程中的具体错误:
{ "Logging": { "LogLevel": { "Microsoft.AspNetCore.SignalR": "Debug", "Microsoft.AspNetCore.Http.Connections": "Debug" } } }
启动服务后查看控制台日志,可获取连接失败的具体原因(如认证失败、路由不存在等)。
5. 验证客户端连接配置
- 确保客户端连接地址与服务端一致,避免HTTP/HTTPS混用。
- 尝试强制使用WebSocket传输,排除其他传输方式干扰:
hubConnection = new HubConnectionBuilder() .WithUrl($"https://localhost:63225/{notificationUrl}", options => { options.AccessTokenProvider = () => GetAccessTokenAsync(); options.Transports = HttpTransportType.WebSockets; }) .Build();
6. 确认CustomUserIdProvider注册
在Program.cs中正确注册自定义用户ID提供者:
builder.Services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
未注册会导致SignalR使用默认提供者,无法正确获取用户标识符。
7. 检查HubContext注入
业务服务中确保IHubContext<NotificationHub>正确注入,避免后续推送功能异常:
private readonly IHubContext<NotificationHub> _hubContext; public YourService(IHubContext<NotificationHub> hubContext) { _hubContext = hubContext; }
内容的提问来源于stack exchange,提问作者khalil hajri
相关产品推荐
相关产品推荐

