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

ASP.NET MVC中如何识别当前视图,实现全页面SignalR消息通知?

解决SignalR根据用户当前页面选择调用不同客户端方法的问题

1. 客户端传递当前页面信息到SignalR Hub

在_Layout.cshtml的SignalR初始化逻辑里,把当前页面的路径(或自定义标识)传递给Hub,有两种实用方式:

方式一:连接时通过查询字符串携带

// _Layout.cshtml中的SignalR初始化脚本
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/msgHub", {
        query: {
            currentPage: window.location.pathname // 示例值:"/Messages/Index"
        }
    })
    .build();

connection.start().catch(err => console.error(err.toString()));

方式二:连接成功后主动调用Hub方法上报

connection.start().then(() => {
    // 连接建立后,调用Hub的SetCurrentPage方法传递当前页面
    return connection.invoke("SetCurrentPage", window.location.pathname);
}).catch(err => console.error(err.toString()));

2. 在MSGHub中维护用户页面状态

用线程安全的字典存储每个连接对应的用户ID和当前页面,确保多并发场景下的安全性:

using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;
using System.Security.Claims;

public class MSGHub : Hub
{
    // 并发安全字典:键=连接ID,值=(用户ID, 当前页面路径)
    private static readonly ConcurrentDictionary<string, (string UserId, string CurrentPage)> _userPageMap = new();

    public override async Task OnConnectedAsync()
    {
        var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (userId == null)
        {
            await base.OnConnectedAsync();
            return;
        }

        // 对应方式一:从查询字符串获取当前页面
        var currentPage = Context.GetHttpContext().Request.Query["currentPage"].ToString();
        _userPageMap.TryAdd(Context.ConnectionId, (userId, currentPage));
        
        await base.OnConnectedAsync();
    }

    // 对应方式二:接收客户端上报的当前页面
    public async Task SetCurrentPage(string currentPage)
    {
        var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (userId == null) return;

        _userPageMap.TryUpdate(Context.ConnectionId, (userId, currentPage), _userPageMap[Context.ConnectionId]);
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        _userPageMap.TryRemove(Context.ConnectionId, out _);
        await base.OnDisconnectedAsync(exception);
    }
}

3. 发送消息时根据页面选择客户端方法

当需要推送新消息时,先从字典中筛选目标用户的所有活跃连接,再根据每个连接的当前页面调用对应方法:

public async Task SendNewMessage(string targetUserId, MessageDto message)
{
    // 找到目标用户的所有在线连接
    var userConnections = _userPageMap.Where(kv => kv.Value.UserId == targetUserId);

    foreach (var connection in userConnections)
    {
        var currentPage = connection.Value.CurrentPage;
        if (currentPage.Equals("/Messages/Index", StringComparison.OrdinalIgnoreCase))
        {
            // 用户在消息页面,调用页面专属的ReceiveMessage方法
            await Clients.Client(connection.Key).SendAsync("ReceiveMessage", message);
        }
        else
        {
            // 用户不在消息页面,调用菜单通知的NotifMessage方法
            await Clients.Client(connection.Key).SendAsync("NotifMessage", message.UnreadCount);
        }
    }
}

额外注意事项

  • 多标签页处理:同一个用户打开多个标签页时,每个标签页对应独立连接,字典会存储多条记录,发送时需遍历所有连接分别处理。
  • 页面切换更新:用户在同一标签页切换页面时,要在目标页面的脚本中调用SetCurrentPage更新状态,比如在Messages/Index.cshtml中添加:
    connection.invoke("SetCurrentPage", window.location.pathname);
    
  • 并发安全:必须使用ConcurrentDictionary存储用户页面映射,避免多线程冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 02:52:36