SignalR Core

SignalR Core. Первое приложение

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

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

Что такое SignalR

SignalR Core представляет библиотеку от компании Microsoft, которая предназначена для создания приложений, работающих в режиме реального времени. В частности, ее можно использовать вместе с ASP.NET Core. SignalR использует двунаправленную связь для обмена сообщениями между клиентом и сервером, благодаря чему сервер может отправлять в режиме реального времени всем клиентам некоторые данные.

Где может использоваться SignalR? Прежде всего это приложения, которые получают данные в реальном режиме времени, например, чаты, социальные сети, игровые приложения, карты, приложения для аукционов, голосований и карт, панели управления, приложения для мониторинга данных и так далее.

Для обмена сообщениями между клиентом и сервером SignalR использует ряд механизмов:

  • WebSockets

  • Server-Side Events

  • Long Polling

Исходя из возможностей клиента и сервера инфраструктура SignalR выбирает наилучший механизм для взаимодействия. В частности, наиболее оптимальным является WebSockets, соответственно если и клиент, и сервер позволяют использовать этот механизм, то взаимодействие идет через WebSockets. Однако если WebSockets не поддерживается, то применяется Server-Side Events. И если SSE не поддерживается, то применяется Long Polling.

Поддерживаемые клиенты

SignalR обеспечивает взаимодействие клиента с сервером. Если на стороне сервера ожидаемое это приложение ASP.NET Core, то на стороне клиента все намного интереснее. В частности, в качестве клиента в SignalR может выступать:

  • Приложение на JavaScript, запущенное на Node.js (поддерживается версия Node.js 8 и выше)

  • Приложение на JavaScript, которое работает в рамках браузеров Google Chrome (в том числе на Android), Microsoft Edge, Mozilla Firefox, Opera, Safari (в том числе на iOS), Internet Explorer (только 11-я версия)

  • Приложение на .NET. Это может быть десктопное приложение WPF, Windows Forms, мобильное приложение Xamarin.

  • Приложение на языке Java

Также в прекрасном SignalR будущего ожидается поддержка приложений на C++ и Swift.

Первое приложение

Создадим новый проект ASP.NET Core по типу Empty:

SignalR в ASP.NET Core

Определение серверной части

При работы с SignalR на стороне сервера необходимо создать специальную сущность - хаб (hub). По сути хаб представляет класс, наследующийся от класса Hub, который может обрабатывать запросы. Создадим новый хаб. Для этого добавим в проект следующий класс ChatHub:

using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace SignalRApp
{
    public class ChatHub : Hub
    {
        public async Task Send(string message)
        {
            await this.Clients.All.SendAsync("Send", message);
        }
    }
}

Класс хаба наследуется от класса Hub. И здесь определен один метод Send(), который получает некоторое отправленное сообщение в виде параметра message и затем с помощью вызова await Clients.All.SendAsync("Send", message) ретранслирует это сообщение всем подключенным клиентам.

Первый параметр метода SendAsync() указывает на метод, который будет получать ответ от сервера, а второй параметр представляет набор значений, которые посылаются в ответе клиенту. То есть метод Send на клиенте получит значение параметра message. То есть наш хаб будет просто получать сообщение и транслировать его всем подключенным клиентам.

Но чтобы SignalR и хаб ChatHub заработали, надо сконфигурировать класс Startup:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace SignalRApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSignalR();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseDeveloperExceptionPage();

            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<ChatHub>("/chat");
            });
        }
    }
}

Чтобы задействовать сервисы SignalR, в методе ConfigureServices вызывает метод:

services.AddSignalR();

А следующий вызов в методе Configure:

app.UseEndpoints(endpoints =>
{
	endpoints.MapHub<ChatHub>("/chat");
});

У объекта IEndpointRouteBuilder вызывается метод MapHub, который позволяет связать запросы и класс хаба. В данном случае он устанавливает класс ChatHub в качестве обработчика запросов по пути "/chat". То есть, чтобы обратиться к хабу, строка запроса должна иметь вид типа "http://localhost:5000/chat".

Стоит отметить, что если адрес сервера и адрес клиента не будут совпадать, то, возможно, потребуется настроить поддержку CORS, о которой речь идет в следующей главе.

Создание клиентской части

Для создания клиентской части можно использовать различные способы. Например, можно использовать javascript, либо же использовать typescript, определить приложение на .NET или Java. В данном случае мы будем использовать JavaScript, который будет выполняться на обычной странице html.

Для хранения статических файлов добавим в проект папку wwwroot.

Прежде всего на стороне клиента javascript нам потребуется подключить специальный js-скрипт. Для хранения всех необходимых javascript вначале создадим в папке wwwroot новый подкаталог js. Далее нажмем на эту папку правой кнопкой мыши и в контекстном меню выберем Add -> Client Side Library

Install signalr core in ASP.NET Core

Далее нам откроется окно добавления клиентских библиотек. Укажем в нем следующие опции:

Установка signalr core в ASP.NET Core

В поле Provider укажем значение unpkg.

В поле Library в качестве названия пакета введем @microsoft/signalr@latest.

Поскольку пакет содержит много файлов, которые нам могут не понадобиться, то отметим пункт Choose specific files: и затем из списка файлов отметим только signalr.min.js, то есть минимизированную версию библиотеки. Но при желании и необходимости можно выбрать и другие файлы.

И в конце в поле Target location укажем расположение, по которому будет сохранена библиотека, то есть путь wwwroot/js/signalr.

В итоге в проекте по пути wwwroot/js/signalr/dist/browser/ мы сможем найти файл signalr.min.js

В качестве альтернативы, особенно, если мы работаем не в Visual Studio, можно было бы загрузить пакет "@microsoft/signalr" через пакетный менеджер NPM, например, определим следующий файл package.json:

{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "devDependencies": {
    "@microsoft/signalr": "3.1.0"
  }
}

Теперь определим клиентскую часть. Добавим в папку wwwroot новый файл index.html. В итоге проект будет выглядеть следующим образом:

Первый проект signalr в Asp.Net Core

На странице index.html определим следующий код:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>SignalR Chat - Metanit.com</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("Send", function (data) {

            let elem = document.createElement("p");
            elem.appendChild(document.createTextNode(data));
            let 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>

На странице определено текстовое поле для ввода сообщение и кнопка для его отправки. Под ними расположен блок chatroom, в который будут добавляться полученные сообщения.

Внизу страницы подключается скрипт "signalr.min.js". Далее в коде javascript определена основная логика взаимодействия клиента с хабом.

Вначале определяется переменная, с помощью которой устанавливается подключение:

const hubConnection = new signalR.HubConnectionBuilder()
            .withUrl("/chat")
            .build();

Для взаимодействия с хабом ChatHub с помощью метода build() объекта HubConnectionBuilder создается объект hubConnection - объект подключения. Метод withUrl устанавливает адрес, по котору приложение будет обращаться к хабу. Поскольку ChatHub на сервере сопоставляется с адресом "/chat", то именно этот адрес и передается в withUrl.

Далее метод hubConnection.on устанавливает метод на стороне клиента, который будет получать данные от сервера:

hubConnection.on("Send", function (data) {

	let elem = document.createElement("p");
	elem.appendChild(document.createTextNode(data));
	let firstElem = document.getElementById("chatroom").firstChild;
	document.getElementById("chatroom").insertBefore(elem, firstElem);

});

В данном случае метод называется Send и фактически он представляют функцию, которая передается в качестве второго параметра. Эта функция принимает один параметр data - те данные, которые в хабе отправляются клиенту. В самой функции с помощью стандартных функций javascript создается элемент. В этот элемент помещается присланное с сервера сообщение. Затем элемент добавляется в начало элемента chatroom.

Далее устанавливается обработчик для кнопки, который вызывается при ее нажатии:

document.getElementById("sendBtn").addEventListener("click", function (e) {
	let message = document.getElementById("message").value;
	hubConnection.invoke("Send", message);
});

Для отправки данных хабу на сервер вызывается метод hubConnection.invoke("Send", message), первый параметр которого представляет метод хаба, обрабатывающий данный запрос, а второй параметр - данные, отправляемые на сервер.

И для начала соединения с сервером вызывается функция hubConnection.start().

После запуска приложения в разных браузерах при отправке сообщения каждый браузер будет получать отправленное сообщение и выводить его на веб-страницу:

SignalR в ASP.NET Core

Теперь модифицируем приложение, что кроме сообщения пользователя также передавалось и его имя. Вначале изменим код класса ChatHub:

using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace SignalRApp
{
    public class ChatHub : Hub
    {
        public async Task Send(string message, string userName)
        {
            await Clients.All.SendAsync("Send", message, userName);
        }
    }
}

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

И изменим страницу index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>SignalR Chat - Metanit.com</title>
</head>
<body>
    <div id="loginBlock">
        Введите логин:<br />
        <input id="userName" type="text" />
        <input id="loginBtn" type="button" value="Войти" />
    </div><br />

    <div id="header"></div><br />

    <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();

        let userName = '';
        // получение сообщения от сервера
        hubConnection.on('Send', function (message, userName) {
 
            // создаем элемент <b> для имени пользователя
            let userNameElem = document.createElement("b");
            userNameElem.appendChild(document.createTextNode(userName + ': '));
 
            // создает элемент <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);
 
        });
 
        // установка имени пользователя
        document.getElementById("loginBtn").addEventListener("click", function (e) {
            userName = document.getElementById("userName").value;
            document.getElementById("header").innerHTML = '<h3>Welcome ' + userName + '</h3>';
        });
        // отправка сообщения на сервер
        document.getElementById("sendBtn").addEventListener("click", function (e) {
            let message = document.getElementById("message").value;
            hubConnection.invoke("Send", message, userName);
        });

        hubConnection.start();
    </script>
</body>
</html>

Так как хаб на сервере отправляет клиентам два значения - имя пользователя и его сообщение, то соответственно на стороне клиента в функции, которая регистрируется в методе hubConnection.on мы можем получить оба этих значения.

И теперь мы условно можем войти под разными пользователями в различных браузерах и отправлять друг другу сообщения:

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