保持 SignalR 连接的方法

Ways to persist SignalR connection

我正在创建网络应用程序,让用户可以通过所谓的聊天进行交流。

为了实现这种通信,我使用了 SignalR 库。我在第一次访问我的页面(主页)时创建连接。因此,创建连接的 JS 代码会创建用于配置连接的变量。

然后用户进入聊天室,这是不同的页面,因此加载了新的 JS 等。持有连接变量的那个现在不可用。但现在我需要该连接才能在我的聊天室中发送消息。

所以这个变量必须 "transfered" 交给下一个脚本。

那么,通过网站的整个会话实际保持连接的方法是什么

在服务器端,您可以通过user id向特定用户发送消息:

var user = await _userManager.GetUserAsync(Context.User);

await Clients.User(user.Id).SendCoreAsync("msg", new object[] { user.Id, user.Email });

在客户端,每当启动连接并侦听集线器 msg 时,用户将收到消息:

connection.on('msg', function (...data) {
    console.log('data:', data);
});

通过用户id发送消息,您无需关心目标用户在哪里。


public class ChatHub : Hub
{
    private IUserManager<ApplicationUser> _userManager;

    public ChatHub(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task GetInfo()
    {
        var user = await _userManager.GetUserAsync(Context.User);

        await Clients.User(user.Id).SendCoreAsync("msg", new object[] { user.Id, user.Email });
    }
}

终于找到答案了。

有人建议我应该使用 ASP NET Identity,这是非常有效的,但我已经创建了简单的身份验证和用户管理。它不安全,也不如 ASP NET Identity 的一半好(我仔细查看并仔细研究了它),但它只是个人项目,而不是生产项目,所以如果它发展,我可能会切换到 Identity 或甚至自己实现一个 ;) 但事实并非如此。

它需要一些额外的步骤,所以:

  1. 我需要在 ASP.NET Core 中启用会话,为此我使用了 this article。这样,我就可以保留我的用户登录名并将其提供给 signalR

    的用户 ID 提供者
  2. 在 .NET 中为 SignalR 添加 cutsom 用户 ID 提供程序:

我需要创建这样的 class

public class UserIdProvider : IUserIdProvider
{
  public static readonly string SESSION_LOGIN_KEY = "loggedUser";
  private readonly IHttpContextAccessor _httpContextAccessor;
  
  public UserIdProvider(IHttpContextAccessor httpContextAccessor) 
  {
    _httpContextAccessor = httpContextAccessor;
  }

  public string GetUserId(HubConnectionContext connection)
  {
    var session = _httpContextAccessor.HttpContext.Session;
    session.TryGetValue(SESSION_LOGIN_KEY, out byte[] loginBA);
    if (loginBA == null)
    {
      return null;
    }

    return new string(loginBA.Select(b => (char)b).ToArray());
  }
}

因此,登录后我可以在 Session 中设置登录,因此它成为“state”变量,并在上面 class.

中使用它

此外,需要在 ASP 服务中添加它,如下所示(在 Startup.ConfigureServices 中):

services.AddSingleton<IUserIdProvider, UserIdProvider>();

还有一件事,仍然需要设置:在UserIdProvider中我们需要通过HttpContext访问Session。为了使用 HttpContext 我们需要像下面这样指定它(也在 Startup.ConfigureServices 中):

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

HttpContextAccessor 传递给 serices 构造函数。

完成所有这些之后,您可以使用在 Context.UserIdnentifier

中设置的登录名访问 SignalR Hub 中的用户

这还可以向特定用户发送消息,只需传递他们的登录信息(前端客户端只需选择用户),如下所示:

public async Task SendMessage(string message, string user)
{
  await Clients.User(user).SendAsync("ReceiveMessage", message).ConfigureAwait(false);
}

注意 不过有一个问题。计算机上的浏览器没有保留会话,我通过(也在 Startup.ConfigureServices 中)解决了这个问题:

services.Configure<CookiePolicyOptions>(options =>
{
  // This lambda determines whether user consent for non-essential cookies is needed for a given request.
  options.CheckConsentNeeded = context => false; // <~~~~ This needs to be set to false
  options.MinimumSameSitePolicy = SameSiteMode.None;
});

没有它,您需要小心使用 cookie,如果您不在站点上接受它们,它将无法工作,因为用户的登录将不会被保留。