Группы в SignalR представляют коллекцию подключений, которые ассоциированы с именем группы. SignalR позволяет добавлять и удалять клиентов из группы. Один клиент может входить сразу в несколько групп.
Для работы с группами в классе Hub определено свойство Groups, которое представляет объект IGroupManager. Он имеет два метода:
AddToGroupAsync(connectionId, groupName): добавляет клиента с идентификатором connectionId в группу с именем groupName
RemoveFromGroupAsync(connectionId, groupName): удаляет клиента с идентификатором connectionId из группы
Также у объекта IHubCallerClients определено ряд методов, которые позволяют выбрать определенные группы для отправки им сообщения. Для вызова метода у клиентов в группах можно использовать ряд методов:
Group(string groupName): вызывает метод у клиентов определенной группы
GroupExcept(string groupName, IReadOnlyList<string> connectionIds): вызывает метод у клиентов группы по имени groupName за исключением тех клиентов, id которых передаются в качестве второго параметра
Groups(IReadOnlyList<string> groupNames): вызывает метод у клиентов групп, названия которых передаются в метод
OthersInGroup(string OthersInGroup): вызывает метод у клиентов определенной группы за исключением текущего клиента
Например, определим следующий хаб:
using Microsoft.AspNetCore.SignalR; namespace SignalRApp { public class ChatHub : Hub { public async Task Enter(string username, string groupName) { await Groups.AddToGroupAsync(Context.ConnectionId, groupName); await Clients.All.SendAsync("Notify", $"{username} вошел в чат в группу {groupName}"); } public async Task Send(string message, string userName, string groupName) { await Clients.Group(groupName).SendAsync("Receive", message, userName); } } }
В хабе определено два метода. В методе Enter хаб получает имя пользователя и группы, в которую он вошел. Имя групп может быть любым, важно только учитывать, что оно регистрозависимое. Пользователь добавляется в эту группу, а всем клиентам отправляется соответствующее сообщение.
В методе Send ретранслируют сообщение клиента всем участникам определенной группы.
Для тестирования можно определить следующую веб-страницу index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="loginBlock"> Введите логин и группу:<br /> <input id="userName" type="text" placeholder="Введите логин" /> <input id="userGroup" type="text" placeholder="Введите группу" /> <input id="loginBtn" type="button" value="Войти" /> </div><br /> <div id="inputForm"> Сообщение:<br /> <input type="text" id="message" /> <input type="button" id="sendBtn" value="Отправить" /> </div> <div id="chatroom"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script> <script> let userName = ""; let userGroup = ""; const hubConnection = new signalR.HubConnectionBuilder() .withUrl("/chat") .build(); // установка имени пользователя и вход в группу document.getElementById("loginBtn").addEventListener("click", function (e) { userName = document.getElementById("userName").value; userGroup = document.getElementById("userGroup").value; hubConnection.invoke("Enter", userName, userGroup); }); // отправка сообщения в группу document.getElementById("sendBtn").addEventListener("click", () => { const message = document.getElementById("message").value; hubConnection.invoke("Send", message, userName, userGroup) .catch(error => console.error(error)); }); // получение сообщения для определенной группы hubConnection.on("Receive", (message, user) => { // создаем элемент <b> для имени пользователя const userNameElem = document.createElement("b"); userNameElem.textContent = `${user}: `; // создает элемент <p> для сообщения пользователя const elem = document.createElement("p"); elem.appendChild(userNameElem); elem.appendChild(document.createTextNode(message)); const firstElem = document.getElementById("chatroom").firstChild; document.getElementById("chatroom").insertBefore(elem, firstElem); }); // получение общего уведомления hubConnection.on("Notify", message => { const elem = document.createElement("p"); elem.textContent = message; const firstElem = document.getElementById("chatroom").firstChild; document.getElementById("chatroom").insertBefore(elem, firstElem); }); hubConnection.start() .then(() => { document.getElementById("sendBtn").disabled = false; document.getElementById("loginBtn").disabled = false }) .catch ((err) => console.error(err)); </script> </body> </html>
Здесь по нажатию на кнопку loginBtn хабу отправляется имя пользователя и его группы. А по нажатию на кнопку sendBtn отправляется сообщение с указанием имени пользователя и его группы.
Для тестирования определим в файле Program.cs следующий код:
using SignalRApp; // пространство имен класса ChatHub var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR(); var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles(); app.MapHub<ChatHub>("/chat"); app.Run();
Пример работы:
Так, в примере выше пользователи Tom и Sam принадлежат группе .net developers, поэтому только они видят сообщения, предназначенные для этой группы. А пользователь Bob, который принадлежит группе "java developers", не видит эти сообщения. Ему доступны только общие сообщения.
При работе с группами стоит учитывать, группа никак не связана с аутентификацией и авторизацией. Например, в примере хаба выше любой подключившийся к хабу и не состояющий в определенной группе, может обратиться к методу Send и отправить сообщение в эту группу, только это сообщение будет видно только членам этой группы. Группа в SignalR - это не более чем условное объединение клиентов, подключенных к хабу. И мы не можем узнать, какие пользователи состоят в той или иной группе, получить всех пользователей определенной группы.
И если, к примеру, необходимо, создавать какие-нибудь групповые чаты, где могут послать сообщения и соответственно видеть эти сообщения только участники данного чата, необходимо использовать стандартные механизмы авторизации и аутентификации. Например:
[Authorize(Roles="admin")] public async Task Send(string message, string username) { await Clients.Group("admins").SendAsync("Send", message, username); }
В данном случае в группу "admins" могут посылать сообщения только авторизованные пользователи, у которых роль - "admin".