You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 11:57:05