Angular+SignalR向特定用户推送通知失败问题求助
问题描述
使用SignalR向所有用户发送通知(Clients.All)可正常执行,但尝试向特定用户发送通知时,后台返回成功提示,前端却无法接收通知。
后端.NET代码
public async Task<IActionResult> SendNotificationToUser(string userName, string message) { using (var scope = _serviceProvider.CreateScope()) { var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>(); var user = await userManager.FindByNameAsync(userName); if (user != null) { await _hubContext.Clients.User(user.Id).SendAsync("ReceiveMessage", message); return Ok(new { Message = "Notification sent to user" }); } return NotFound(new { Message = "User not found" }); } }
前端Angular代码
export class SignalRService { private hubConnection: HubConnection | undefined; private messageSubject: Subject<string> = new Subject<string>(); constructor(private authService: AuthenticationService) {} startConnection(): void { if (typeof localStorage !== 'undefined') { const token = localStorage.getItem('token') || ''; const options: IHttpConnectionOptions = { accessTokenFactory: () => token }; this.hubConnection = new HubConnectionBuilder() .withUrl('https://localhost:7163/Notify', options) .build(); this.hubConnection.start() .then(() => { console.log('Connexion établie avec succès'); this.hubConnection?.on('ReceiveMessage', (message: string) => { console.log(message); this.messageSubject.next(message); }); }) .catch(err => { console.error('Erreur lors du démarrage de la connexion : ', err); }); this.hubConnection.onclose((error) => { console.log('Connexion fermée : ', error); setTimeout(() => { this.startConnection(); }, 5000); }); } else { console.error('localStorage is not available in this environment.'); } } getMessageSubject(): Subject<string> { return this.messageSubject; } sendNotificationToUser(user: string, message: string): void { if (this.hubConnection) { this.hubConnection.invoke('SendNotificationToUser', user, message) .catch(err => console.error(err)); } else { console.error('Hub connection is not initialized.'); } } }
解决方案
以下是几个排查和修复的关键点:
验证SignalR用户标识映射
SignalR默认用ClaimTypes.NameIdentifier作为用户ID标识,确保JWT Token中包含该Claim,且值与ApplicationUser.Id完全匹配。可在Hub的OnConnectedAsync方法中添加调试代码,输出当前连接用户的Claim:public override async Task OnConnectedAsync() { var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; Console.WriteLine($"Connected user ID: {userId}"); await base.OnConnectedAsync(); }对比输出的ID与
userManager.FindByNameAsync获取的user.Id是否一致。检查认证中间件顺序
后端需先配置认证授权中间件,再配置SignalR,确保请求先完成身份验证:app.UseAuthentication(); app.UseAuthorization(); // 之后配置SignalR app.MapHub<NotifyHub>("/Notify");确认前端Token有效性
在浏览器控制台打印token,用JWT解析工具查看Token是否过期、是否包含正确的用户标识Claim。自定义用户ID Claim类型(可选)
若系统使用自定义Claim作为用户ID,可在SignalR配置中指定:services.AddSignalR(options => { options.UserIdProvider = context => context.User.FindFirst("your-custom-user-id-claim")?.Value; });通过连接ID调试发送逻辑
在Hub中维护用户ID与连接ID的映射,发送时直接使用连接ID测试:private static readonly Dictionary<string, string> _userConnections = new(); public override async Task OnConnectedAsync() { var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrEmpty(userId)) { _userConnections.TryAdd(userId, Context.ConnectionId); } await base.OnConnectedAsync(); } // 发送时替换为连接ID await _hubContext.Clients.Client(connectionId).SendAsync("ReceiveMessage", message);
内容的提问来源于stack exchange,提问作者Rachad
相关产品推荐
相关产品推荐

