保持 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 或甚至自己实现一个 ;) 但事实并非如此。
它需要一些额外的步骤,所以:
我需要在 ASP.NET Core 中启用会话,为此我使用了 this article。这样,我就可以保留我的用户登录名并将其提供给 signalR
的用户 ID 提供者
在 .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,如果您不在站点上接受它们,它将无法工作,因为用户的登录将不会被保留。
我正在创建网络应用程序,让用户可以通过所谓的聊天进行交流。
为了实现这种通信,我使用了 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 或甚至自己实现一个 ;) 但事实并非如此。
它需要一些额外的步骤,所以:
我需要在 ASP.NET Core 中启用会话,为此我使用了 this article。这样,我就可以保留我的用户登录名并将其提供给 signalR
的用户 ID 提供者在 .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
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,如果您不在站点上接受它们,它将无法工作,因为用户的登录将不会被保留。