Контекст хаба, подключение и отключение клиентов

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 24.12.2019

В классе 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" диагностические уведомления.

Таким образом, при подключение и отключение мы сможем уведомлять всех клиентов:

Подключение и отключение клиентов в SignalR

Стоит отметить, что методы OnConnectedAsync срабатывает еще до первой отправки сообщения пользователем. Метод OnDisconnectedAsync срабатывает, если клиент закроет вкладку браузера или перезагрузит страницу.

Также стоит отметить, что если мы откроем в одном браузере две вкладки со страницей, то у нас будут два отдельных подключенных клиента, которые будут иметь разные идентификаторы.

Получение информации о контексте HTTP

С помощью метода 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 таким образом мы можем, а вот установить заголовки или поменять их значение или установить те же куки нельзя.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850