Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
В классе Hub определено свойство Context, которое представляет тип HubCallerContext и который позволяет нам получить некоторую информацию о подключении к хабу. Данная информация хранится в следующих его свойствах:
ConnectionId: возвращает уникальный идентификатор подключения в виде строки.
ConnectionAborted: возвращает объект CancellationToken, который извещает о закрытии подключения
User: возвращает объект ClaimsPrincipal, ассоциированный с текущим
пользователем. По сути это аналог свойства HttpContext.User
, которое доступно в констроллерах
UserIdentifier: возвращает идентификатор пользователя. По умолчанию индентификатор пользователя представляет клейм ClaimTypes.NameIdentifier объекта ClaimsPrincipal, который ассоциирован с данным подключением.
Items: возвращает словарь значений, ассоциированных с текущим подключением. Данный словарь позволяет хранить данные для определенного клиента между его запросами
Features: возвращает коллекцию возможностей HTTP, ассоциированных с текущим подключением
Также класс HubCallerContext определяет пару методов:
Abort(): принудительно завершает текущее подключение
GetHttpContext(): возвращает объект HttpContext для текущего подключения или null, если подключение не ассоциировано с запросом HTTP.
Класс Hub определяет два метода, которые мы можем переопределить в классах-наследниках:
OnConnectedAsync(): срабатывает при подключении нового клиента
OnDisconnectedAsync(Exception exception): срабатывает при отключении клиента, в качестве параметра передается сообщение об ошибке, которая описывает, почему произошло отключение.
Реализуем эти методы и определим следующий класс хаба:
using Microsoft.AspNetCore.SignalR; using System; using System.Diagnostics; using System.Threading.Tasks; namespace SignalRApp { public class ChatHub : Hub { public async Task Send(string message) { await Clients.All.SendAsync("Receive", message, Context.ConnectionId); } public override async Task OnConnectedAsync() { await Clients.All.SendAsync("Notify", $"{Context.ConnectionId} вошел в чат"); await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception exception) { await Clients.All.SendAsync("Notify", $"{Context.ConnectionId} покинул в чат"); await base.OnDisconnectedAsync(exception); } } }
В данном случае вместо условного ника пользователя будет использоваться идентификатор подключения. В методах OnConnectedAsync и OnDisconnectedAsync мы будем уведомлять клиентов, о том, что какой-то клиент подключился или отключился от приложения.
Для теста определим следующую веб-страницу index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>SignalR Chat</title> </head> <body> <div id="inputForm"> <input type="text" id="message" /> <input type="button" id="sendBtn" value="Отправить" /> </div> <div id="chatroom"></div> <script src="js/signalr/dist/browser/signalr.min.js"></script> <script> const hubConnection = new signalR.HubConnectionBuilder() .withUrl("/chat") .build(); // получение сообщения от сервера hubConnection.on('Receive', function (message, connectionId) { // создаем элемент <b> для имени идентификатора подключения let userNameElem = document.createElement("b"); userNameElem.appendChild(document.createTextNode(connectionId + ": ")); // создает элемент <p> для сообщения пользователя let elem = document.createElement("p"); elem.appendChild(userNameElem); elem.appendChild(document.createTextNode(message)); var firstElem = document.getElementById("chatroom").firstChild; document.getElementById("chatroom").insertBefore(elem, firstElem); }); hubConnection.on('Notify', function (message) { // добавляет элемент для диагностического сообщения let notifyElem = document.createElement("b"); notifyElem.appendChild(document.createTextNode(message)); let elem = document.createElement("p"); elem.appendChild(notifyElem); var firstElem = document.getElementById("chatroom").firstChild; document.getElementById("chatroom").insertBefore(elem, firstElem); }); // отправка сообщения на сервер document.getElementById("sendBtn").addEventListener("click", function (e) { let message = document.getElementById("message").value; hubConnection.invoke('Send', message); }); hubConnection.start(); </script> </body> </html>
Здесь в javascript в функции "Send" получаем обычные сообщения от клиентов, а в функции "Notify" диагностические уведомления.
Таким образом, при подключение и отключение мы сможем уведомлять всех клиентов:
Стоит отметить, что методы OnConnectedAsync срабатывает еще до первой отправки сообщения пользователем. Метод OnDisconnectedAsync срабатывает, если клиент закроет вкладку браузера или перезагрузит страницу.
Также стоит отметить, что если мы откроем в одном браузере две вкладки со страницей, то у нас будут два отдельных подключенных клиента, которые будут иметь разные идентификаторы.
С помощью метода GetHttpContext()
контекста хаба мы можем получить различную информацию о запросе, например, информацию о браузере пользователя,
его ip, куки и т.д., вообщем различные заголовки http. Например:
using Microsoft.AspNetCore.SignalR; using System; using System.Diagnostics; using System.Threading.Tasks; namespace SignalRApp { public class ChatHub : Hub { public async Task Send(string message) { await Clients.All.SendAsync("Receive", message, Context.ConnectionId); } public override async Task OnConnectedAsync() { var context = this.Context.GetHttpContext(); // получаем кук name if (context.Request.Cookies.ContainsKey("name")) { string userName; if(context.Request.Cookies.TryGetValue("name", out userName)) { Debug.WriteLine($"name = {userName}"); } } // получаем юзер-агент Debug.WriteLine($"UserAgent = {context.Request.Headers["User-Agent"]}"); // получаем ip Debug.WriteLine($"RemoteIpAddress = {context.Connection.RemoteIpAddress.ToString()}"); await base.OnConnectedAsync(); } } }
Следует отметить, что получить заголовки http таким образом мы можем, а вот установить заголовки или поменять их значение или установить те же куки нельзя.