使用SignalR集线器向特定连接的用户广播消息
本文关键字:用户 广播 消息 连接 SignalR 集线器 使用 | 更新日期: 2023-09-27 18:07:48
我的目标是当数据库发生插入/更新时,立即通知连接的用户。
我有一个Hub
。
[Authorize]
public class NotificationsHub : Hub
{
private readonly IWorker _worker;
public NotificationsHub() : this(new WorkerFacade(new ExtremeDataRepository())) { }
public NotificationsHub(IWorker worker)
{
_worker = worker;
}
public override Task OnConnected()
{
if (!HubUsersManager.ConnectedUsers.Any(p => p.Name.Equals(Context.User.Identity.Name)))
{
HubUsersManager.ConnectedUsers.Add(new HubUserHandler
{
Name = Context.User.Identity.Name,
ConnectionId = Context.ConnectionId
});
}
return base.OnConnected();
}
//public override Task OnDisconnected()
//{
// HubUserHandler hubUserHandler =
// HubUsersManager.ConnectedUsers.FirstOrDefault(p => p.Name == Context.User.Identity.Name);
// if (hubUserHandler != null)
// HubUsersManager.ConnectedUsers.Remove(hubUserHandler);
// return base.OnDisconnected();
//}
public async Task<ICollection<NotificationMessage>> GetAllPendingNotifications(string userId)
{
return await _worker.GetAllPendingNotifications(userId);
}
public void UpdateNotificationMessagesToAllClients(ICollection<NotificationMessage> notificationMessages)
{
Clients.All.updateNotificationMessages(notificationMessages);
}
}
正如你所看到的,我从连接用户的手动映射中删除了OnDisconnected,以使用户名和ConnectionId稍后用于我的实体框架会员用户广播消息,因为每次客户端离开集线器而不是实际断开连接时都会调用OnDisconnected,例如关闭浏览器。
SignalR的初始化工作完美,我测试了它,GetAllPendingNotifications
在连接开始时被调用,我调用SignalR Javascript代码的函数,见下面的代码。
// A simple templating method for replacing placeholders enclosed in curly braces.
if (!String.prototype.supplant) {
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
};
}
if (typeof String.prototype.endsWith !== 'function') {
String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
}
$(function () {
// Reference the auto-generated proxy for the hub.
var notificationsHub = $.connection.notificationsHub;
var userName, userId;
// Create a function that the hub can call back to display messages.
notificationsHub.client.updateNotificationMessages = updateNotificationsToPage;
function updateNotificationsToPage(notificationMessages) {
alert('Im in updating notifications');
if (notificationMessages.count > 0) {
$.each(function(index) {
alert($(this).Message);
});
}
};
function init() {
notificationsHub.server.getAllPendingNotifications(userId).done(updateNotificationsToPage);
}
function userDetailsRetrieved(data) {
userName = data.UserName;
userId = data.Id;
// Start the connection.
$.connection.hub.start().done(init);
}
function getUserDetails() {
var baseURL = document.baseURI,
url;
if (!baseURL.endsWith('Home/GetUserDetails')) {
if (baseURL.endsWith('/'))
url = baseURL + 'Home/GetUserDetails';
else if (baseURL.endsWith('/Home') ||
baseURL.endsWith('/Home/'))
url = baseURL + '/GetUserDetails';
else {
url = baseURL + '/Home/GetUserDetails';
}
} else
url = baseURL;
$.ajax({
url: url, success: userDetailsRetrieved, type: 'POST', error: function () {
console.log(arguments);
}, dataType: 'json' });
}
getUserDetails();
});
我知道映射可能不是必要的,我可以覆盖实现将其映射到我实际的ApplicationUser
GUID Id以避免它,但现在它似乎服务于目的,如果我首先使它工作,可能会改变它。
您还可以看到IWorker
引用,如果客户机调用服务器方法,我使用它来获取一些数据。此外,IWorker
的具体实现正在被所有Controllers
使用,并且具有Clients
属性,因此我可以立即将消息广播回连接的客户端。
private IHubConnectionContext _clients;
private IHubConnectionContext Clients
{
get {
return _clients ?? (_clients = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>().Clients);
}
}
已正确填充。在应用程序的某个点,IWorker
调用Clients
属性。
foreach (HubUserHandler hubUserHandler in HubUsersManager.ConnectedUsers)
{
ApplicationUser user = await GetApplicationUserWithName(hubUserHandler.Name);
List<string> userRoles = await GetUserRoles(user);
bool isInRole = userRoles.Any(p => p.Equals("ARole"));
List<NotificationMessage> testList = new List<NotificationMessage>
{
new NotificationMessage {Message = "TEST MESSAGE!"}
};
if (isInRole)
Clients.Client(hubUserHandler.ConnectionId).updateNotificationMessages(testList);
}
我的浏览器什么都没有,应该弹出两个警报,一个说Im in updating notifications
,一个说TEST MESSAGE
,就像你在上面的Javascript
代码中看到的那样。
[编辑]
更改代码发送消息到特定的Group
,但没有再次显示到我的客户端浏览器。
public override Task OnConnected()
{
if (_groupManager.Count == 0)
{
ICollection<string> rolesCollection = _worker.GetAllRoles();
foreach (string roleName in rolesCollection)
{
_groupManager.Add(roleName);
}
}
foreach (string groupRole in _groupManager.Groups)
{
if (Context.User.IsInRole(groupRole))
{
Groups.Add(Context.ConnectionId, groupRole);
}
}
return base.OnConnected();
}
在我的worker类的某个地方:
List<NotificationMessage> testList = new List<NotificationMessage>
{
new NotificationMessage {Message = "TEST MESSAGE!"}
};
Clients.Group("TestGroup").updateNotificationMessages(testList);
我想我错过了什么!
我的两分钱:你看过Clients.User()
方法吗?它已经为您完成了所有ConnectionId
映射,并且您正在使用Context.User
,因此它应该开箱即用。如果您需要更复杂的东西,您可以在IUserIdProvider
的自定义实现中编写自己的映射逻辑。至少你可以消除一个层次的复杂性(但如果问题不是因为这个,就不确定它是否能解决你的具体问题)。这只是一个想法。