You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

从Controller调用SignalR Core Hub方法时的Connection ID问题

咱们来一步步拆解你问的这些SignalR相关问题:

一、控制器里调用HubContext.Clients.Caller/Others的实际效果

先明确:CallerOthers这两个属性是为Hub类的方法设计的——在Hub方法里,Caller指向发起当前Hub调用的那个客户端,Others则是除了这个Caller之外的所有连接客户端。

但在普通API控制器里直接用这两个属性,基本是无效的。因为控制器处理的是普通HTTP请求,并没有绑定SignalR的连接上下文,此时Caller对应的connection ID是空值或者无效的默认值,Others自然也没法正确筛选出目标客户端,相当于调用了个空对象的方法,不会有任何消息发送出去。

二、这种场景下的connection ID是什么?

刚才也提到了,控制器的HTTP请求和SignalR的长连接是两个独立的会话,控制器里没有SignalR连接的上下文信息,所以HubContext.Clients.Caller依赖的connection ID是空值或者无效值,根本指向不了任何已连接的SignalR客户端,这就是这两个属性失效的核心原因。

三、控制器动作中如何获取当前API客户端的connection ID?

因为普通API请求和SignalR连接本身不共享上下文,所以得通过以下两种方式来关联:

  • 方式一:客户端主动传递connection ID
    客户端在成功建立SignalR连接后,能拿到自己的connection ID(比如在客户端的连接成功回调里获取,或者在Hub的OnConnectedAsync方法里返回给客户端)。之后调用API时,把这个connection ID放在请求头、URL参数或者请求体里传递过来。控制器里就能直接拿到并使用:

    [HttpPost("send-message")]
    public async Task<IActionResult> SendMessageToClient([FromHeader] string signalRConnectionId)
    {
        if (!string.IsNullOrWhiteSpace(signalRConnectionId))
        {
            await _hubContext.Clients.Client(signalRConnectionId)
                .SendAsync("ReceiveNotification", "消息来自API控制器");
        }
        return Ok();
    }
    
  • 方式二:通过认证用户关联connection ID
    如果你的客户端是已认证用户,可以在SignalR连接建立时,把用户ID和对应的connection ID存储起来(比如用内存缓存、Redis或者数据库)。之后在控制器里,通过当前认证用户的ID查询到对应的connection ID,再给目标客户端发消息:

    // Hub中连接建立时的代码
    public override async Task OnConnectedAsync()
    {
        var userId = Context.User.Identity?.Name;
        if (!string.IsNullOrWhiteSpace(userId))
        {
            // 这里用缓存示例,实际可以根据需求选存储方式
            await _cache.SetStringAsync($"User_{userId}_ConnectionId", Context.ConnectionId);
        }
        await base.OnConnectedAsync();
    }
    
    // 控制器中的代码
    [HttpPost("send-to-user")]
    public async Task<IActionResult> SendMessageToCurrentUser()
    {
        var userId = User.Identity?.Name;
        if (!string.IsNullOrWhiteSpace(userId))
        {
            var connectionId = await _cache.GetStringAsync($"User_{userId}_ConnectionId");
            if (!string.IsNullOrWhiteSpace(connectionId))
            {
                await _hubContext.Clients.Client(connectionId)
                    .SendAsync("ReceiveNotification", "针对当前用户的API消息");
            }
        }
        return Ok();
    }
    

    注意:如果一个用户同时在多设备登录,可能会有多个connection ID,这种情况需要存储一个列表,给所有关联的连接发消息。


内容的提问来源于stack exchange,提问作者HappyNomad

火山引擎 最新活动