IHubContext

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

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

Класс хаба - не единственное место, где мы можем отправлять сообщения клиентам. SignalR позволяет это делать также в контроллерах, страницах Razor Pages, компонентах middleware, сервисах благодаря внедрению зависимости IHubContext. Объект IHubContext реализует часть функциональности стандартного класса хаба, в частности, в нем определены такие же свойства Groups и Clients, которые позволяют манипулировать группами и отправлять сообщения всем подключенным клиентам.

Например, определим хаб ChatHub:

using Microsoft.AspNetCore.SignalR;

namespace SignalRApp
{
    public class ChatHub : Hub
    {
    }
}

Несмотря на то, что класс хаба пустой, он нам понадобится для типизиации объекта IHubContext, кроме того, взаимодействие с клиентами будет идти через данный хаб.

Далее добавим в классе Startup функциональность SignalR и MVC:

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

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

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

            app.UseRouting();

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

Определим следующий контроллер HomeController:

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

namespace SignalRApp.Controllers
{
    public class HomeController : Controller
    {
        IHubContext<ChatHub> hubContext;
        public HomeController(IHubContext<ChatHub> hubContext)
        {
            this.hubContext = hubContext;
        }
        
        public IActionResult Index()
        {
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> Create(string product)
        {
            await hubContext.Clients.All.SendAsync("Notify", $"Добавлено: {product} - {DateTime.Now.ToShortTimeString()}");
            return RedirectToAction("Index");
        }
    }
}

Благодаря встроенному механизму внедрения зависимостей мы можем получить объект IHubContext в конструкторе контроллера. Основное ограничение в данном случае - этот объект должен быть типизирован классом хаба.

Затем в методах контроллера мы можем использовать данный IHubContext, например, отправив сообщение подключенным клиентам. К примеру, в методе Create происходит условное добавление некоторого товара, и все подключенные клиенты получают уведомление

Для тестирования определим представление Index.cshtml:


@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Главная</title>
</head>
<body>

    <form action='@Url.Action("Create")' method="post">
        <input type="text" name="product" />
        <input type="submit" value="Отправить" />
    </form>

    <div id="notify"></div>

    <script src="js/signalr/dist/browser/signalr.min.js"></script>
    <script>
        const hubConnection = new signalR.HubConnectionBuilder()
            .withUrl("/chat")
            .build();

        // получение сообщения от сервера
        hubConnection.on('Notify', function (message) {

            // создает элемент <p> для сообщения пользователя
            let elem = document.createElement("p");
            elem.appendChild(document.createTextNode(message));

            document.getElementById("notify").appendChild(elem);

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

В представлении определена форма для отправки данных на метод Create и блок для вывода сообщений, получаемых от хаба. Причем стоит отметить, что подключение идет по адресу "/chat" - то есть фактически к хабу ChatHub. При получении уведомлений от хаба будет вызываться функция notify.

И после отправки формы все остальные подключенные клиенты увидят информацию об отправленных данных.

HubContext в SignalR в ASP.NET Core

Подобным образом можно получать сервис IHubContext в других сервисах, компонентах middleware и страницах Razor Pages.

Однако стоит отметить, что данный способ отправки сообщений клиентам имее некоторые недостатки. Например, у свойства Clients доступно только свойство All - то есть мы можем отправить сообщение только всем клиентам и нету таких свойств как Other или Caller, которые доступны при использовании в классе хаба. Также мы не можем получить из IHubContext id подключения. Поэтому если нам потребуется в контроллерах или других классах получить id подключения, то его можно получить при подключении на клиенте и затем пересылать тем или иным образом, например, через куки или через параметры запроса.

К примеру, определим следующий контроллер:

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

namespace SignalRApp.Controllers
{
    public class HomeController : Controller
    {
        IHubContext<ChatHub> hubContext;
        public HomeController(IHubContext<ChatHub> hubContext)
        {
            this.hubContext = hubContext;
        }

        public IActionResult Index()
        {
            return View();
        }
        [HttpPost]
        public async Task Create(string product, string connectionId)
        {
            await hubContext.Clients.AllExcept(connectionId).SendAsync("Notify", $"Добавлено: {product} - {DateTime.Now.ToShortTimeString()}");
            await hubContext.Clients.Client(connectionId).SendAsync("Notify", $"Ваш товар добавлен!");
        }
    }
}

Теперь метод Create также получае id подключение - параметр connectionId и посылает клиентам в зависимости от id подключения разные сообщения.

Также изменим представление:

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Главная</title>
</head>
<body>

    <form method="post">
        <input type="text" name="product" id="productField" />
        <input type="submit" value="Отправить" id="submitForm" />
    </form>

    <div id="notify"></div>

    <script src="js/signalr/dist/browser/signalr.min.js"></script>
    <script>
        const hubConnection = new signalR.HubConnectionBuilder()
            .withUrl("/chat")
            .build();
        let connectionId = "";
        document.getElementById("submitForm")
            .addEventListener("click", function (e) {
                e.preventDefault();

                const data = new FormData();
                data.append("product", document.getElementById("productField").value);
                data.append("connectionId", connectionId);
                
                fetch("home/create", {
                    method: "POST",
                    body: data
                })
                .catch(error => console.error("Error: ", error));
            });
        // получение сообщения от сервера
        hubConnection.on("Notify", function (message) {

            // создает элемент <p> для сообщения пользователя
            let elem = document.createElement("p");
            elem.appendChild(document.createTextNode(message));

            document.getElementById("notify").appendChild(elem);

        });
        hubConnection.start().then(() => {
            // после соединения получаем id подключения
            console.log(hubConnection.connectionId);
            connectionId = hubConnection.connectionId;
        });
    </script>
</body>
</html>

Теперь страница отправляет ajax-запросы к методу Create контроллера Home. После подключения к хабу через функцию hubConnection.start() получаем id подключения. И затем при отправке включаем его в тело запроса.

SignalR и HubContext в ASP.NET Core MVC
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850