Поделиться через


Использование хабов SignalR для ASP.NET Core

Рейчел Аппель и Кевин Гриффин

SignalR API Hubs позволяет подключенным клиентам вызывать методы на сервере, обеспечивая обмен данными в режиме реального времени. Сервер определяет методы, вызываемые клиентом, и клиент определяет методы, вызываемые сервером. SignalR кроме того, обеспечивает непрямую связь между клиентами и клиентами, всегда опосредованную SignalR центром, позволяя отправлять сообщения между отдельными клиентами, группами или всеми подключенными клиентами. SignalR позаботится обо всем необходимом, чтобы обеспечить возможность обмена данными между клиентами и серверами в режиме реального времени.

Настройка SignalR центров

Чтобы зарегистрировать службы, необходимые для центров SignalR, вызовите AddSignalR в Program.cs.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

Чтобы настроить SignalR конечные точки, вызовите MapHubтакже в Program.cs:

app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");

app.Run();

Note

Серверные сборки ASP.NET Core теперь устанавливаются с пакетом SDK для .NET Core. См. SignalR сборки в общем фреймворке для получения дополнительной информации.

Создание и использование центров

Создайте концентратор, объявив класс, наследуемый от Hub. Добавьте public методы в класс, чтобы сделать их вызываемыми из клиентов:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Note

Центры являются временными:

  • Не сохраняйте состояние в свойстве класса концентратора. Каждый вызов метода концентратора выполняется в новом экземпляре концентратора.
  • Не создавайте экземпляр хаба непосредственно с помощью внедрения зависимостей. Для отправки сообщений клиенту из другого места в приложении используется IHubContext.
  • Используйте await при вызове асинхронных методов, которые зависят от поддержания работоспособности хаба. Например, метод Clients.All.SendAsync(...) может завершиться ошибкой, если его вызывают без await, и метод концентратора завершается до завершения SendAsync.

Объект Context

Класс Hub содержит Context свойство, содержащее следующие свойства со сведениями о подключении:

Property Description
ConnectionId Возвращает уникальный идентификатор подключения, назначенный SignalR. Для каждого подключения существует один идентификатор подключения.
UserIdentifier Возвращает идентификатор пользователя. По умолчанию SignalR использует ClaimTypes.NameIdentifier из ClaimsPrincipal связанного с подключением в качестве идентификатора пользователя.
User Возвращает ClaimsPrincipal, связанный с текущим пользователем.
Items Возвращает коллекцию ключей и значений, которую можно использовать для совместного использования данных в области этого подключения. Данные можно хранить в этой коллекции, и они будут сохраняться для подключения между различными вызовами методов концентратора.
Features Возвращает коллекцию функций, доступных в соединении. Сейчас эта коллекция не требуется в большинстве сценариев, поэтому она еще не описана.
ConnectionAborted Получает объект CancellationToken, который уведомляет о прерывании подключения.

Hub.Context также содержит следующие методы:

Method Description
GetHttpContext Возвращает HttpContext для подключения или null, если подключение не связано с HTTP-запросом. Для HTTP-подключений используйте этот метод для получения таких сведений, как заголовки HTTP и строки запроса.
Abort Прерывает подключение.

Объект Клиенты

Класс Hub содержит свойство, содержащее следующие свойства для обмена данными между сервером Clients и клиентом:

Property Description
All Вызывает метод для всех подключенных клиентов
Caller Вызывает метод у клиента, который инициировал вызов метода концентратора.
Others Вызывает метод для всех подключенных клиентов, кроме клиента, вызвавшего этот метод

Hub.Clients также содержит следующие методы:

Method Description
AllExcept Вызывает метод для всех подключенных клиентов, за исключением указанных подключений.
Client Вызывает метод для определенного подключенного клиента.
Clients Вызывает метод для определенных подключенных клиентов
Group Вызывает метод для всех подключений в указанной группе
GroupExcept Вызывает метод для всех подключений в указанной группе, за исключением указанных подключений.
Groups Вызывает метод для нескольких групп подключений
OthersInGroup Вызывает метод в группе подключений, исключая клиента, который инициировал вызов метода узла.
User Вызывает метод для всех подключений, связанных с конкретным пользователем
Users Вызывает метод для всех подключений, связанных с указанными пользователями

Каждое свойство или метод в предыдущих таблицах возвращает объект с методом SendAsync . Метод SendAsync получает имя метода клиента для вызова и любых параметров.

Объект, возвращаемый методами Client и Caller, также содержит метод InvokeAsync, который можно использовать, чтобы дождаться результата от клиента.

Отправка сообщений клиентам

Чтобы выполнить вызовы к определенным клиентам, используйте свойства Clients объекта. В следующем примере существуют три узловых метода:

  • SendMessage отправляет сообщение всем подключенным клиентам с помощью Clients.All.
  • SendMessageToCaller отправляет сообщение обратно вызывающей стороне с помощью Clients.Caller.
  • SendMessageToGroup отправляет сообщение всем клиентам в SignalR Users группе.
public async Task SendMessage(string user, string message)
    => await Clients.All.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToCaller(string user, string message)
    => await Clients.Caller.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToGroup(string user, string message)
    => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Строго типизированные центры

Недостаток использования SendAsync заключается в том, что он использует строку для указания вызываемого метода клиента. При этом код остается открытым для ошибок среды выполнения, если имя метода пропущено или отсутствует в клиенте.

Альтернативой использованию SendAsync является строго типизированный класс Hub с Hub<T>. В следующем примере метод ChatHub клиента был выделен в интерфейс под названием IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Этот интерфейс можно использовать для рефакторинга предыдущего примера ChatHub, чтобы он стал строго типизированным.

public class StronglyTypedChatHub : Hub<IChatClient>
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.ReceiveMessage(user, message);

    public async Task SendMessageToCaller(string user, string message)
        => await Clients.Caller.ReceiveMessage(user, message);

    public async Task SendMessageToGroup(string user, string message)
        => await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

Использование Hub<IChatClient> включает проверку времени компиляции клиентских методов. Это предотвращает проблемы, вызванные использованием строк, так как Hub<T> может предоставлять доступ только к методам, определенным в интерфейсе. Использование строго типизированного Hub<T> отключает возможность использования SendAsync.

Note

Суффикс Async не удаляется из имен методов. Если метод клиента не определен с помощью .on('MyMethodAsync'), не используйте MyMethodAsync в качестве имени.

Результаты клиента

Помимо вызова клиентов, сервер может запросить результат от клиента. Для этого требуется, чтобы сервер использовал ISingleClientProxy.InvokeAsync, а клиент возвращал результат от своего обработчика .On.

Существует два способа использования API на сервере, во-первых — вызов Client(...) или Caller на свойстве Clients в методе Hub.

public class ChatHub : Hub
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        var message = await Clients.Client(connectionId).InvokeAsync<string>(
            "GetMessage");
        return message;
    }
}

Второй способ — вызвать Client(...) на экземпляре IHubContext<T>:

async Task SomeMethod(IHubContext<MyHub> context)
{
    string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
        "GetMessage");
}

Строго типизированные концентраторы также могут возвращать значения из методов интерфейса:

public interface IClient
{
    Task<string> GetMessage();
}

public class ChatHub : Hub<IClient>
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        string message = await Clients.Client(connectionId).GetMessage();
        return message;
    }
}

Клиенты возвращают результаты в обработчиках .On(...), как показано ниже:

Клиент .NET

hubConnection.On("GetMessage", async () =>
{
    Console.WriteLine("Enter message:");
    var message = await Console.In.ReadLineAsync();
    return message;
});

Клиент Typescript

hubConnection.on("GetMessage", async () => {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("message");
        }, 100);
    });
    return promise;
});

Клиент на Java

hubConnection.onWithResult("GetMessage", () -> {
    return Single.just("message");
});

Изменение имени метода хаба

По умолчанию имя метода концентратора сервера — это имя метода .NET. Чтобы изменить это поведение по умолчанию для определенного метода, используйте атрибут HubMethodName . Клиент должен использовать это имя вместо имени метода .NET при вызове метода:

[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
    => await Clients.User(user).SendAsync("ReceiveMessage", user, message);

Внедрение служб в концентратор

Конструкторы хабов могут принимать службы из системы внедрения зависимостей в виде параметров, которые можно хранить в свойства класса для использования в методах хаба.

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

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message, IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Если не требуется неявное разрешение параметров из служб, отключите его с DisableImplicitFromServicesParameters. Чтобы явно указать, какие параметры разрешаются из DI в методах концентратора, используйте опцию DisableImplicitFromServicesParameters и атрибут [FromServices] или настраиваемый атрибут, реализующий IFromServiceMetadata, на параметрах метода концентратора, которые должны разрешаться из DI.

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message,
        [FromServices] IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Note

Эта функция использует IServiceProviderIsService, который может быть реализован при необходимости реализациями DI. Если контейнер DI приложения не поддерживает эту функцию, внедрение служб в методы концентратора не поддерживается.

Поддержка ключевых служб в инъекции зависимостей

Ключевые службы — это механизм регистрации и получения служб внедрения зависимостей (DI) посредством использования ключей. Служба связана с ключом путем вызова AddKeyedSingleton (или AddKeyedScopedAddKeyedTransient) для регистрации. Доступ к зарегистрированной службе путем указания ключа с атрибутом [FromKeyedServices] . В следующем коде показано, как использовать ключи служб:

using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

app.MapRazorPages();
app.MapHub<MyHub>("/myHub");

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

public class MyHub : Hub
{
    public void SmallCacheMethod([FromKeyedServices("small")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }

    public void BigCacheMethod([FromKeyedServices("big")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }
}

Обработка событий для подключения

SignalR API системы Центров предоставляет OnConnectedAsync и OnDisconnectedAsync виртуальные методы для управления и отслеживания соединений. Переопределите виртуальный OnConnectedAsync метод для выполнения действий, когда клиент подключается к центру, например добавление его в группу:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Переопределите виртуальный OnDisconnectedAsync метод для выполнения действий при отключении клиента. Если клиент намеренно отключается, например путем вызова connection.stop(), exception параметр имеет значение null. Однако если клиент отключается из-за ошибки, например сбоя сети, exception параметр содержит исключение, описывающее сбой:

public override async Task OnDisconnectedAsync(Exception? exception)
{
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync не нужно вызывать в OnDisconnectedAsync, это обрабатывается автоматически.

Управление ошибками

Исключения, возникающие в методах хабов, отправляются клиенту, который вызвал метод. В клиенте JavaScript метод invoke возвращает JavaScript Promise. Клиенты могут присоединить catch обработчик к возвращаемому обещанию или использовать try/catch для async/await обработки исключений.

try {
  await connection.invoke("SendMessage", user, message);
} catch (err) {
  console.error(err);
}

Подключения не закрываются, когда концентратор создает исключение. По умолчанию SignalR возвращается универсальное сообщение об ошибке клиенту, как показано в следующем примере:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.

Непредвиденные исключения часто содержат конфиденциальную информацию, например, имя сервера базы данных в случае, если соединение с базой данных прерывается. SignalR не предоставляет эти подробные сообщения об ошибках по умолчанию в качестве меры безопасности. Дополнительные сведения о том, почему сведения об исключении подавляются, см.: в разделе "Вопросы безопасности" в ASP.NET Core SignalR.

Если исключительное условие должно распространяться на клиент, используйте HubException класс. HubException Если в методе концентратора создается исключение, SignalRотправляется клиенту все сообщение об исключении, не измененное:

public Task ThrowException()
    => throw new HubException("This error will be sent to the client!");

Note

SignalR отправляет клиенту только свойство Message исключения. Трассировка стека и другие свойства исключения недоступны клиенту.

Дополнительные ресурсы

Рейчел Аппель и Кевин Гриффин

SignalR API Hubs позволяет подключенным клиентам вызывать методы на сервере, обеспечивая обмен данными в режиме реального времени. Сервер определяет методы, вызываемые клиентом, и клиент определяет методы, вызываемые сервером. SignalR кроме того, обеспечивает непрямую связь между клиентами и клиентами, всегда опосредованную SignalR центром, позволяя отправлять сообщения между отдельными клиентами, группами или всеми подключенными клиентами. SignalR позаботится обо всем необходимом, чтобы обеспечить возможность обмена данными между клиентами и серверами в режиме реального времени.

Настройка SignalR центров

Чтобы зарегистрировать службы, необходимые для центров SignalR, вызовите AddSignalR в Program.cs.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

Чтобы настроить SignalR конечные точки, вызовите MapHubтакже в Program.cs:

app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");

app.Run();

Note

Серверные сборки ASP.NET Core теперь устанавливаются с пакетом SDK для .NET Core. См. SignalR сборки в общем фреймворке для получения дополнительной информации.

Создание и использование центров

Создайте концентратор, объявив класс, наследуемый от Hub. Добавьте public методы в класс, чтобы сделать их вызываемыми из клиентов:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Note

Центры являются временными:

  • Не сохраняйте состояние в свойстве класса концентратора. Каждый вызов метода концентратора выполняется в новом экземпляре концентратора.
  • Не создавайте экземпляр хаба непосредственно с помощью внедрения зависимостей. Для отправки сообщений клиенту из другого места в приложении используется IHubContext.
  • Используйте await при вызове асинхронных методов, которые зависят от поддержания работоспособности хаба. Например, метод Clients.All.SendAsync(...) может завершиться ошибкой, если его вызывают без await, и метод концентратора завершается до завершения SendAsync.

Объект Context

Класс Hub содержит Context свойство, содержащее следующие свойства со сведениями о подключении:

Property Description
ConnectionId Возвращает уникальный идентификатор подключения, назначенный SignalR. Для каждого подключения существует один идентификатор подключения.
UserIdentifier Возвращает идентификатор пользователя. По умолчанию SignalR использует ClaimTypes.NameIdentifier из ClaimsPrincipal связанного с подключением в качестве идентификатора пользователя.
User Возвращает ClaimsPrincipal, связанный с текущим пользователем.
Items Возвращает коллекцию ключей и значений, которую можно использовать для совместного использования данных в области этого подключения. Данные можно хранить в этой коллекции, и они будут сохраняться для подключения между различными вызовами методов концентратора.
Features Возвращает коллекцию функций, доступных в соединении. Сейчас эта коллекция не требуется в большинстве сценариев, поэтому она еще не описана.
ConnectionAborted Получает CancellationToken, который уведомляет о прерывании подключения.

Hub.Context также содержит следующие методы:

Method Description
GetHttpContext Возвращает HttpContext для подключения или null, если подключение не связано с HTTP-запросом. Для HTTP-подключений используйте этот метод для получения таких сведений, как заголовки HTTP и строки запроса.
Abort Прерывает подключение.

Объект Client

Класс Hub содержит свойство, содержащее следующие свойства для обмена данными между сервером Clients и клиентом:

Property Description
All Вызывает метод для всех подключенных клиентов
Caller Вызывает метод на клиенте, который вызвал метод хаба.
Others Вызывает метод для всех подключенных клиентов, исключая клиента, который вызвал метод

Hub.Clients также содержит следующие методы:

Method Description
AllExcept Вызывает метод для всех подключенных клиентов, кроме указанных подключений.
Client Вызывает метод для определенного подключенного клиента.
Clients Вызывает метод для определенных подключенных клиентов
Group Вызывает метод для всех подключений в указанной группе
GroupExcept Вызывает метод для всех подключений в указанной группе, за исключением указанных подключений.
Groups Вызывает метод для нескольких групп подключений
OthersInGroup Вызывает метод в группе подключений, исключая клиента, который инициировал вызов метода узла.
User Вызывает метод для всех подключений, связанных с конкретным пользователем
Users Вызывает метод для всех подключений, связанных с указанными пользователями

Каждое свойство или метод в предыдущих таблицах возвращает объект с методом SendAsync . Метод SendAsync получает имя метода клиента для вызова и любых параметров.

Объект, возвращаемый методами Client и Caller, также содержит метод InvokeAsync, который можно использовать, чтобы дождаться результата от клиента.

Отправка сообщений клиентам

Чтобы выполнить вызовы к определенным клиентам, используйте свойства Clients объекта. В следующем примере существуют три узловых метода:

  • SendMessage отправляет сообщение всем подключенным клиентам с помощью Clients.All.
  • SendMessageToCaller отправляет сообщение обратно вызывающей стороне с помощью Clients.Caller.
  • SendMessageToGroup отправляет сообщение всем клиентам в SignalR Users группе.
public async Task SendMessage(string user, string message)
    => await Clients.All.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToCaller(string user, string message)
    => await Clients.Caller.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToGroup(string user, string message)
    => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Строго типизированные центры

Недостаток использования SendAsync заключается в том, что он использует строку для указания вызываемого метода клиента. При этом код остается открытым для ошибок среды выполнения, если имя метода пропущено или отсутствует в клиенте.

Альтернативой использованию SendAsync является строго типизированный класс Hub с Hub<T>. В следующем примере метод ChatHub клиента был выделен в интерфейс под названием IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Этот интерфейс можно использовать для рефакторинга предыдущего примера ChatHub, чтобы он стал строго типизированным.

public class StronglyTypedChatHub : Hub<IChatClient>
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.ReceiveMessage(user, message);

    public async Task SendMessageToCaller(string user, string message)
        => await Clients.Caller.ReceiveMessage(user, message);

    public async Task SendMessageToGroup(string user, string message)
        => await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

Использование Hub<IChatClient> включает проверку времени компиляции клиентских методов. Это предотвращает проблемы, вызванные использованием строк, так как Hub<T> может предоставлять доступ только к методам, определенным в интерфейсе. Использование строго типизированного Hub<T> отключает возможность использования SendAsync.

Note

Суффикс Async не удаляется из имен методов. Если метод клиента не определен с помощью .on('MyMethodAsync'), не используйте MyMethodAsync в качестве имени.

Результаты клиента

Помимо вызова клиентов, сервер может запросить результат от клиента. Для этого требуется, чтобы сервер использовал ISingleClientProxy.InvokeAsync, а клиент возвращал результат от своего обработчика .On.

Существует два способа использования API на сервере, во-первых — вызов Client(...) или Caller на свойстве Clients в методе Hub.

public class ChatHub : Hub
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        var message = await Clients.Client(connectionId).InvokeAsync<string>(
            "GetMessage");
        return message;
    }
}

Второй способ — вызвать Client(...) на экземпляре IHubContext<T>:

async Task SomeMethod(IHubContext<MyHub> context)
{
    string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
        "GetMessage");
}

Строго типизированные концентраторы также могут возвращать значения из методов интерфейса:

public interface IClient
{
    Task<string> GetMessage();
}

public class ChatHub : Hub<IClient>
{
    public async Task<string> WaitForMessage(string connectionId)
    {
        string message = await Clients.Client(connectionId).GetMessage();
        return message;
    }
}

Клиенты возвращают результаты в обработчиках .On(...), как показано ниже:

Клиент .NET

hubConnection.On("GetMessage", async () =>
{
    Console.WriteLine("Enter message:");
    var message = await Console.In.ReadLineAsync();
    return message;
});

Клиент Typescript

hubConnection.on("GetMessage", async () => {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("message");
        }, 100);
    });
    return promise;
});

Клиент на Java

hubConnection.onWithResult("GetMessage", () -> {
    return Single.just("message");
});

Изменение имени метода хаба

По умолчанию имя метода концентратора сервера — это имя метода .NET. Чтобы изменить это поведение по умолчанию для определенного метода, используйте атрибут HubMethodName . Клиент должен использовать это имя вместо имени метода .NET при вызове метода:

[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
    => await Clients.User(user).SendAsync("ReceiveMessage", user, message);

Внедрение служб в концентратор

Конструкторы хабов могут принимать службы из системы внедрения зависимостей в виде параметров, которые можно хранить в свойства класса для использования в методах хаба.

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

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message, IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Если не требуется неявное разрешение параметров из служб, отключите его с DisableImplicitFromServicesParameters. Чтобы явно указать, какие параметры разрешаются из DI в методах концентратора, используйте опцию DisableImplicitFromServicesParameters и атрибут [FromServices] или настраиваемый атрибут, реализующий IFromServiceMetadata, на параметрах метода концентратора, которые должны разрешаться из DI.

services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

// ...

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message,
        [FromServices] IDatabaseService dbService)
    {
        var userName = dbService.GetUserName(user);
        return Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

Note

Эта функция использует IServiceProviderIsService, который может быть реализован при необходимости реализациями DI. Если контейнер DI приложения не поддерживает эту функцию, внедрение служб в методы концентратора не поддерживается.

Обработка событий подключения

SignalR API системы Центров предоставляет OnConnectedAsync и OnDisconnectedAsync виртуальные методы для управления и отслеживания соединений. Переопределите виртуальный OnConnectedAsync метод для выполнения действий, когда клиент подключается к центру, например добавление его в группу:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Переопределите виртуальный OnDisconnectedAsync метод для выполнения действий при отключении клиента. Если клиент намеренно отключается, например путем вызова connection.stop(), exception параметр имеет значение null. Однако если клиент отключается из-за ошибки, например сбоя сети, exception параметр содержит исключение, описывающее сбой:

public override async Task OnDisconnectedAsync(Exception? exception)
{
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync не нужно вызывать в OnDisconnectedAsync, это обрабатывается автоматически.

Управление ошибками

Исключения, возникающие в методах хабов, отправляются клиенту, который вызвал метод. В клиенте JavaScript метод invoke возвращает JavaScript Promise. Клиенты могут присоединить catch обработчик к возвращаемому обещанию или использовать try/catch для async/await обработки исключений.

try {
  await connection.invoke("SendMessage", user, message);
} catch (err) {
  console.error(err);
}

Подключения не закрываются, когда концентратор создает исключение. По умолчанию SignalR возвращается универсальное сообщение об ошибке клиенту, как показано в следующем примере:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.

Непредвиденные исключения часто содержат конфиденциальную информацию, например, имя сервера базы данных в случае, если соединение с базой данных прерывается. SignalR не предоставляет эти подробные сообщения об ошибках по умолчанию в качестве меры безопасности. Дополнительные сведения о том, почему сведения об исключении подавляются, см.: в разделе "Вопросы безопасности" в ASP.NET Core SignalR.

Если исключительное условие должно распространяться на клиент, используйте HubException класс. HubException Если в методе концентратора создается исключение, SignalRотправляется клиенту все сообщение об исключении, не измененное:

public Task ThrowException()
    => throw new HubException("This error will be sent to the client!");

Note

SignalR отправляет клиенту только свойство Message исключения. Трассировка стека и другие свойства исключения недоступны клиенту.

Дополнительные ресурсы

Рейчел Аппель и Кевин Гриффин

SignalR API Hubs позволяет подключенным клиентам вызывать методы на сервере, обеспечивая обмен данными в режиме реального времени. Сервер определяет методы, вызываемые клиентом, и клиент определяет методы, вызываемые сервером. SignalR кроме того, обеспечивает непрямую связь между клиентами и клиентами, всегда опосредованную SignalR центром, позволяя отправлять сообщения между отдельными клиентами, группами или всеми подключенными клиентами. SignalR позаботится обо всем необходимом, чтобы обеспечить возможность обмена данными между клиентами и серверами в режиме реального времени.

Настройка SignalR центров

Чтобы зарегистрировать службы, необходимые для центров SignalR, вызовите AddSignalR в Program.cs.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

Чтобы настроить SignalR конечные точки, вызовите MapHubтакже в Program.cs:

app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");

app.Run();

Note

Серверные сборки ASP.NET Core теперь устанавливаются с пакетом SDK для .NET Core. См. SignalR сборки в общем фреймворке для получения дополнительной информации.

Создание и использование центров

Создайте концентратор, объявив класс, наследуемый от Hub. Добавьте public методы в класс, чтобы сделать их вызываемыми из клиентов:

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Note

Центры являются временными:

  • Не сохраняйте состояние в свойстве класса концентратора. Каждый вызов метода концентратора выполняется в новом экземпляре концентратора.
  • Не создавайте экземпляр хаба непосредственно с помощью внедрения зависимостей. Для отправки сообщений клиенту из другого места в приложении используется IHubContext.
  • Используйте await при вызове асинхронных методов, которые зависят от поддержания работоспособности хаба. Например, метод Clients.All.SendAsync(...) может завершиться ошибкой, если его вызывают без await, и метод концентратора завершается до завершения SendAsync.

Объект Context

Класс Hub содержит Context свойство, содержащее следующие свойства со сведениями о подключении:

Property Description
ConnectionId Возвращает уникальный идентификатор подключения, назначенный SignalR. Для каждого подключения существует один идентификатор подключения.
UserIdentifier Возвращает идентификатор пользователя. По умолчанию SignalR использует ClaimTypes.NameIdentifier из ClaimsPrincipal связанного с подключением в качестве идентификатора пользователя.
User Возвращает ClaimsPrincipal, связанный с текущим пользователем.
Items Возвращает коллекцию ключей и значений, которую можно использовать для совместного использования данных в области этого подключения. Данные можно хранить в этой коллекции, и они будут сохраняться для подключения между различными вызовами методов концентратора.
Features Возвращает коллекцию функций, доступных в соединении. Сейчас эта коллекция не требуется в большинстве сценариев, поэтому она еще не описана.
ConnectionAborted Получает CancellationToken, который уведомляет о прерывании подключения.

Hub.Context также содержит следующие методы:

Method Description
GetHttpContext Возвращает HttpContext для подключения или null, если подключение не связано с HTTP-запросом. Для HTTP-подключений используйте этот метод для получения таких сведений, как заголовки HTTP и строки запроса.
Abort Прерывает подключение.

Объект Client

Класс Hub содержит свойство, содержащее следующие свойства для обмена данными между сервером Clients и клиентом:

Property Description
All Вызывает метод для всех подключенных клиентов
Caller Вызывает метод на клиенте, который вызвал метод хаба.
Others Вызывает метод для всех подключенных клиентов, исключая клиента, который вызвал метод

Hub.Clients также содержит следующие методы:

Method Description
AllExcept Вызывает метод для всех подключенных клиентов, кроме указанных подключений.
Client Вызывает метод для определенного подключенного клиента.
Clients Вызывает метод для определенных подключенных клиентов
Group Вызывает метод для всех подключений в указанной группе
GroupExcept Вызывает метод для всех подключений в указанной группе, за исключением указанных подключений.
Groups Вызывает метод для нескольких групп подключений
OthersInGroup Вызывает метод в группе подключений, исключая клиента, который инициировал вызов метода узла.
User Вызывает метод для всех подключений, связанных с конкретным пользователем
Users Вызывает метод для всех подключений, связанных с указанными пользователями

Каждое свойство или метод в предыдущих таблицах возвращает объект с методом SendAsync . Метод SendAsync получает имя метода клиента для вызова и любых параметров.

Отправка сообщений клиентам

Чтобы выполнить вызовы к определенным клиентам, используйте свойства Clients объекта. В следующем примере существуют три узловых метода:

  • SendMessage отправляет сообщение всем подключенным клиентам с помощью Clients.All.
  • SendMessageToCaller отправляет сообщение обратно вызывающей стороне с помощью Clients.Caller.
  • SendMessageToGroup отправляет сообщение всем клиентам в SignalR Users группе.
public async Task SendMessage(string user, string message)
    => await Clients.All.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToCaller(string user, string message)
    => await Clients.Caller.SendAsync("ReceiveMessage", user, message);

public async Task SendMessageToGroup(string user, string message)
    => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Строго типизированные центры

Недостаток использования SendAsync заключается в том, что он использует строку для указания вызываемого метода клиента. При этом код остается открытым для ошибок среды выполнения, если имя метода пропущено или отсутствует в клиенте.

Альтернативой использованию SendAsync является строго типизированный класс Hub с Hub<T>. В следующем примере метод ChatHub клиента был выделен в интерфейс под названием IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Этот интерфейс можно использовать для рефакторинга предыдущего примера ChatHub, чтобы он стал строго типизированным.

public class StronglyTypedChatHub : Hub<IChatClient>
{
    public async Task SendMessage(string user, string message)
        => await Clients.All.ReceiveMessage(user, message);

    public async Task SendMessageToCaller(string user, string message)
        => await Clients.Caller.ReceiveMessage(user, message);

    public async Task SendMessageToGroup(string user, string message)
        => await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

Использование Hub<IChatClient> включает проверку времени компиляции клиентских методов. Это предотвращает проблемы, вызванные использованием строк, так как Hub<T> может предоставлять доступ только к методам, определенным в интерфейсе. Использование строго типизированного Hub<T> отключает возможность использования SendAsync.

Note

Суффикс Async не удаляется из имен методов. Если метод клиента не определен с помощью .on('MyMethodAsync'), не используйте MyMethodAsync в качестве имени.

Изменение имени метода хаба

По умолчанию имя метода концентратора сервера — это имя метода .NET. Чтобы изменить это поведение по умолчанию для определенного метода, используйте атрибут HubMethodName . Клиент должен использовать это имя вместо имени метода .NET при вызове метода:

[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
    => await Clients.User(user).SendAsync("ReceiveMessage", user, message);

Обработка событий подключения

SignalR API системы Центров предоставляет OnConnectedAsync и OnDisconnectedAsync виртуальные методы для управления и отслеживания соединений. Переопределите виртуальный OnConnectedAsync метод для выполнения действий, когда клиент подключается к центру, например добавление его в группу:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Переопределите виртуальный OnDisconnectedAsync метод для выполнения действий при отключении клиента. Если клиент намеренно отключается, например путем вызова connection.stop(), exception параметр имеет значение null. Однако если клиент отключается из-за ошибки, например сбоя сети, exception параметр содержит исключение, описывающее сбой:

public override async Task OnDisconnectedAsync(Exception? exception)
{
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync не нужно вызывать в OnDisconnectedAsync, это обрабатывается автоматически.

Управление ошибками

Исключения, возникающие в методах хабов, отправляются клиенту, который вызвал метод. В клиенте JavaScript метод invoke возвращает JavaScript Promise. Клиенты могут присоединить catch обработчик к возвращаемому обещанию или использовать try/catch для async/await обработки исключений.

try {
  await connection.invoke("SendMessage", user, message);
} catch (err) {
  console.error(err);
}

Подключения не закрываются, когда концентратор создает исключение. По умолчанию SignalR возвращается универсальное сообщение об ошибке клиенту, как показано в следующем примере:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.

Непредвиденные исключения часто содержат конфиденциальную информацию, например, имя сервера базы данных в случае, если соединение с базой данных прерывается. SignalR не предоставляет эти подробные сообщения об ошибках по умолчанию в качестве меры безопасности. Дополнительные сведения о том, почему сведения об исключении подавляются, см.: в разделе "Вопросы безопасности" в ASP.NET Core SignalR.

Если исключительное условие должно распространяться на клиент, используйте HubException класс. HubException Если в методе концентратора создается исключение, SignalRотправляется клиенту все сообщение об исключении, не измененное:

public Task ThrowException()
    => throw new HubException("This error will be sent to the client!");

Note

SignalR отправляет клиенту только свойство Message исключения. Трассировка стека и другие свойства исключения недоступны клиенту.

Дополнительные ресурсы

Рейчел Аппель и Кевин Гриффин

Просмотреть или скачать образец кода (описание загрузки)

Что такое SignalR концентратор

SignalR API Hubs позволяет подключенным клиентам вызывать методы на сервере, обеспечивая обмен данными в режиме реального времени. Сервер определяет методы, вызываемые клиентом, и клиент определяет методы, вызываемые сервером. SignalR кроме того, обеспечивает непрямую связь между клиентами и клиентами, всегда опосредованную SignalR центром, позволяя отправлять сообщения между отдельными клиентами, группами или всеми подключенными клиентами. SignalR позаботится обо всем необходимом, чтобы обеспечить возможность обмена данными между клиентами и серверами в режиме реального времени.

Настройка SignalR центров

Промежуточному ПО SignalR требуются некоторые службы, которые настраиваются путем вызова AddSignalR:

services.AddSignalR();

При добавлении SignalR функциональности в приложение ASP.NET Core настройте SignalR маршруты, вызвав MapHub в обратном вызове метода Startup.ConfigureUseEndpoints.

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

Note

Серверные сборки ASP.NET Core теперь устанавливаются с пакетом SDK для .NET Core. См. SignalR сборки в общем фреймворке для получения дополнительной информации.

Создание и использование центров

Создайте концентратор, объявив класс, наследуемый от Hub, и добавьте в него открытые методы. Клиенты могут вызывать методы, определенные как public:

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message)
    {
        return Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

Можно указать возвращаемый тип и параметры, включая сложные типы и массивы, как и в любом методе C#. SignalR обрабатывает сериализацию и десериализацию сложных объектов и массивов в параметрах и возвращаемых значениях.

Note

Центры являются временными:

  • Не сохраняйте состояние в свойстве в классе концентратора. Каждый вызов метода концентратора выполняется в новом экземпляре концентратора.
  • Не создавайте экземпляр хаба непосредственно с помощью внедрения зависимостей. Для отправки сообщений клиенту из другого места в приложении используется IHubContext.
  • Используйте await при вызове асинхронных методов, которые зависят от поддержания работоспособности хаба. Например, метод Clients.All.SendAsync(...) может завершиться ошибкой, если его вызывают без await, и метод концентратора завершается до завершения SendAsync.

Объект Context

Класс Hub имеет Context свойство, содержащее следующие свойства со сведениями о подключении:

Property Description
ConnectionId Возвращает уникальный идентификатор подключения, назначенный SignalR. Для каждого подключения существует один идентификатор подключения.
UserIdentifier Возвращает идентификатор пользователя. По умолчанию SignalR использует ClaimTypes.NameIdentifier из ClaimsPrincipal связанного с подключением в качестве идентификатора пользователя.
User Возвращает ClaimsPrincipal, связанный с текущим пользователем.
Items Возвращает коллекцию ключей и значений, которую можно использовать для совместного использования данных в области этого подключения. Данные можно хранить в этой коллекции, и они будут сохраняться для подключения между различными вызовами методов концентратора.
Features Возвращает коллекцию функций, доступных в соединении. Сейчас эта коллекция не требуется в большинстве сценариев, поэтому она еще не описана.
ConnectionAborted Получает CancellationToken, который уведомляет о прерывании подключения.

Hub.Context также содержит следующие методы:

Method Description
GetHttpContext Возвращает HttpContext для подключения или null, если подключение не связано с HTTP-запросом. Для HTTP-подключений этот метод можно использовать для получения таких сведений, как заголовки HTTP и строки запроса.
Abort Прерывает подключение.

Объект Client

Класс Hub имеет свойство, содержащее следующие свойства для обмена данными между сервером Clients и клиентом:

Property Description
All Вызывает метод для всех подключенных клиентов
Caller Вызывает метод на клиенте, который вызвал метод хаба.
Others Вызывает метод для всех подключенных клиентов, исключая клиента, который вызвал метод

Hub.Clients также содержит следующие методы:

Method Description
AllExcept Вызывает метод для всех подключенных клиентов, кроме указанных подключений.
Client Вызывает метод для определенного подключенного клиента.
Clients Вызывает метод для определенных подключенных клиентов
Group Вызывает метод для всех подключений в указанной группе
GroupExcept Вызывает метод для всех подключений в указанной группе, за исключением указанных подключений.
Groups Вызывает метод для нескольких групп подключений
OthersInGroup Вызывает метод в группе подключений, исключая клиента, который инициировал вызов метода узла.
User Вызывает метод для всех подключений, связанных с конкретным пользователем
Users Вызывает метод для всех подключений, связанных с указанными пользователями

Каждое свойство или метод в предыдущих таблицах возвращает объект с методом SendAsync . Этот SendAsync метод позволяет указать имя и параметры вызываемого метода клиента.

Отправка сообщений клиентам

Чтобы выполнить вызовы к определенным клиентам, используйте свойства Clients объекта. В следующем примере есть три метода Hub:

  • SendMessage отправляет сообщение всем подключенным клиентам с помощью Clients.All.
  • SendMessageToCaller отправляет сообщение обратно вызывающей стороне с помощью Clients.Caller.
  • SendMessageToGroup отправляет сообщение всем клиентам в SignalR Users группе.
public Task SendMessage(string user, string message)
{
    return Clients.All.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToCaller(string user, string message)
{
    return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToGroup(string user, string message)
{
    return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}

Строго типизированные центры

Недостаток использования SendAsync заключается в том, что он использует магическую строку для указания вызываемого метода клиента. При этом код остается открытым для ошибок среды выполнения, если имя метода пропущено или отсутствует в клиенте.

Альтернативой использованию SendAsync является строгая типизация Hub с помощью Hub<T>. В следующем примере методы ChatHub клиента были извлечены в интерфейс под названием IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Этот интерфейс можно использовать для рефакторинга предыдущего ChatHub примера:

    public class StronglyTypedChatHub : Hub<IChatClient>
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.ReceiveMessage(user, message);
        }

        public Task SendMessageToCaller(string user, string message)
        {
            return Clients.Caller.ReceiveMessage(user, message);
        }
}

Использование Hub<IChatClient> включает проверку времени компиляции клиентских методов. Это предотвращает проблемы, вызванные использованием магических строк, так как Hub<T> может предоставлять доступ только к методам, определенным в интерфейсе.

Использование строго типизированного Hub<T> отключает возможность использования SendAsync. Все методы, определенные в интерфейсе, по-прежнему могут быть определены как асинхронные. На самом деле, каждый из этих методов должен возвращать Task. Так как это интерфейс, не используйте ключевое async слово. Рассмотрим пример.

public interface IClient
{
    Task ClientMethod();
}

Note

Суффикс Async не удаляется из имени метода. Если метод клиента не определен с помощью .on('MyMethodAsync'), вам не следует использовать MyMethodAsync в качестве имени.

Изменение имени метода хаба

По умолчанию имя метода концентратора сервера — это имя метода .NET. Однако атрибут HubMethodName можно использовать для изменения этого значения по умолчанию и вручную указать имя метода. Клиент должен использовать это имя вместо имени метода .NET при вызове метода:

[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
    return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}

Обработка событий подключения

SignalR API системы Центров предоставляет OnConnectedAsync и OnDisconnectedAsync виртуальные методы для управления и отслеживания соединений. Переопределите виртуальный OnConnectedAsync метод для выполнения действий, когда клиент подключается к Центру, например добавление его в группу:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Переопределите виртуальный OnDisconnectedAsync метод для выполнения действий при отключении клиента. Если клиент намеренно отключается (например, вызывая connection.stop()), параметр exception станет null. Однако если клиент отключен из-за ошибки (например, сбой сети), exception параметр будет содержать исключение, описывающее сбой:

public override async Task OnDisconnectedAsync(Exception exception)
{
    await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync не нужно вызывать в OnDisconnectedAsync, это обрабатывается автоматически.

Warning

Предупреждение безопасности: предоставление ConnectionId доступа может привести к вредоносному олицетворению, если SignalR сервер или версия клиента ASP.NET Core 2.2 или более ранней.

Управление ошибками

Исключения, возникающие в методах хаба, отправляются клиенту, который вызвал метод. В клиенте JavaScript метод invoke возвращает JavaScript Promise. Когда клиент получает ошибку с обработчиком, прикрепленным к обещанию, обработчик catch вызывается и ему передается объект JavaScript Error.

connection.invoke("SendMessage", user, message).catch(err => console.error(err));

Если центр создает исключение, подключения не закрываются. По умолчанию SignalR возвращается универсальное сообщение об ошибке клиенту. Рассмотрим пример.

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.

Непредвиденные исключения часто содержат конфиденциальную информацию, например, имя сервера базы данных в случае, если соединение с базой данных прерывается. SignalR не предоставляет эти подробные сообщения об ошибках по умолчанию в качестве меры безопасности. Дополнительные сведения о том, почему сведения об исключении подавляются, см.: в разделе "Вопросы безопасности" в ASP.NET Core SignalR.

Если у вас есть исключительное условие, которое вы хотите распространить на клиент, можно использовать HubException класс. Если вы выбрасываете HubException из метода концентратора, SignalRотправит неизменённое сообщение клиенту:

public Task ThrowException()
{
    throw new HubException("This error will be sent to the client!");
}

Note

SignalR отправляет клиенту только свойство Message исключения. Трассировка стека и другие свойства исключения недоступны клиенту.

Дополнительные ресурсы

Рейчел Аппель и Кевин Гриффин

Просмотреть или скачать образец кода (описание загрузки)

Что такое SignalR концентратор

SignalR API Hubs позволяет подключенным клиентам вызывать методы на сервере, обеспечивая обмен данными в режиме реального времени. Сервер определяет методы, вызываемые клиентом, и клиент определяет методы, вызываемые сервером. SignalR кроме того, обеспечивает непрямую связь между клиентами и клиентами, всегда опосредованную SignalR центром, позволяя отправлять сообщения между отдельными клиентами, группами или всеми подключенными клиентами. SignalR позаботится обо всем необходимом, чтобы обеспечить возможность обмена данными между клиентами и серверами в режиме реального времени.

Настройка SignalR центров

Промежуточному ПО SignalR требуются некоторые службы, которые настраиваются путем вызова AddSignalR:

services.AddSignalR();

При добавлении SignalR функциональности в приложение ASP.NET Core настройте SignalR маршруты, вызвав UseSignalR в методе Startup.Configure:

app.UseSignalR(route =>
{
    route.MapHub<ChatHub>("/chathub");
});

Создание и использование центров

Создайте концентратор, объявив класс, наследуемый от Hub, и добавьте в него открытые методы. Клиенты могут вызывать методы, определенные как public:

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message)
    {
        return Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

Можно указать возвращаемый тип и параметры, включая сложные типы и массивы, как и в любом методе C#. SignalR обрабатывает сериализацию и десериализацию сложных объектов и массивов в параметрах и возвращаемых значениях.

Note

Центры являются временными:

  • Не сохраняйте состояние в свойстве в классе концентратора. Каждый вызов метода концентратора выполняется в новом экземпляре концентратора.
  • Не создавайте экземпляр хаба непосредственно с помощью внедрения зависимостей. Для отправки сообщений клиенту из другого места в приложении используется IHubContext.
  • Используйте await при вызове асинхронных методов, которые зависят от поддержания работоспособности хаба. Например, метод Clients.All.SendAsync(...) может завершиться ошибкой, если его вызывают без await, и метод концентратора завершается до завершения SendAsync.

Объект Context

Класс Hub имеет Context свойство, содержащее следующие свойства со сведениями о подключении:

Property Description
ConnectionId Возвращает уникальный идентификатор подключения, назначенный SignalR. Для каждого подключения существует один идентификатор подключения.
UserIdentifier Возвращает идентификатор пользователя. По умолчанию SignalR использует ClaimTypes.NameIdentifier из ClaimsPrincipal связанного с подключением в качестве идентификатора пользователя.
User Возвращает ClaimsPrincipal, связанный с текущим пользователем.
Items Возвращает коллекцию ключей и значений, которую можно использовать для совместного использования данных в области этого подключения. Данные можно хранить в этой коллекции, и они будут сохраняться для подключения между различными вызовами методов концентратора.
Features Возвращает коллекцию функций, доступных в соединении. Сейчас эта коллекция не требуется в большинстве сценариев, поэтому она еще не описана.
ConnectionAborted Получает CancellationToken, который уведомляет о прерывании подключения.

Hub.Context также содержит следующие методы:

Method Description
GetHttpContext Возвращает HttpContext для подключения или null, если подключение не связано с HTTP-запросом. Для HTTP-подключений этот метод можно использовать для получения таких сведений, как заголовки HTTP и строки запроса.
Abort Прерывает подключение.

Объект Client

Класс Hub имеет свойство, содержащее следующие свойства для обмена данными между сервером Clients и клиентом:

Property Description
All Вызывает метод для всех подключенных клиентов
Caller Вызывает метод на клиенте, который вызвал метод хаба.
Others Вызывает метод для всех подключенных клиентов, исключая клиента, который вызвал метод

Hub.Clients также содержит следующие методы:

Method Description
AllExcept Вызывает метод для всех подключенных клиентов, кроме указанных подключений.
Client Вызывает метод для определенного подключенного клиента.
Clients Вызывает метод для определенных подключенных клиентов
Group Вызывает метод для всех подключений в указанной группе
GroupExcept Вызывает метод для всех подключений в указанной группе, за исключением указанных подключений.
Groups Вызывает метод для нескольких групп подключений
OthersInGroup Вызывает метод в группе подключений, исключая клиента, который инициировал вызов метода узла.
User Вызывает метод для всех подключений, связанных с конкретным пользователем
Users Вызывает метод для всех подключений, связанных с указанными пользователями

Каждое свойство или метод в предыдущих таблицах возвращает объект с методом SendAsync . Этот SendAsync метод позволяет указать имя и параметры вызываемого метода клиента.

Отправка сообщений клиентам

Чтобы выполнить вызовы к определенным клиентам, используйте свойства Clients объекта. В следующем примере есть три метода Hub:

  • SendMessage отправляет сообщение всем подключенным клиентам с помощью Clients.All.
  • SendMessageToCaller отправляет сообщение обратно вызывающей стороне с помощью Clients.Caller.
  • SendMessageToGroup отправляет сообщение всем клиентам в SignalR Users группе.
public Task SendMessage(string user, string message)
{
    return Clients.All.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToCaller(string user, string message)
{
    return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}

public Task SendMessageToGroup(string user, string message)
{
    return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}

Строго типизированные центры

Недостаток использования SendAsync заключается в том, что он использует магическую строку для указания вызываемого метода клиента. При этом код остается открытым для ошибок среды выполнения, если имя метода пропущено или отсутствует в клиенте.

Альтернативой использованию SendAsync является строгая типизация Hub с помощью Hub<T>. В следующем примере методы ChatHub клиента были извлечены в интерфейс под названием IChatClient.

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Этот интерфейс можно использовать для рефакторинга предыдущего ChatHub примера:

    public class StronglyTypedChatHub : Hub<IChatClient>
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.ReceiveMessage(user, message);
        }

        public Task SendMessageToCaller(string user, string message)
        {
            return Clients.Caller.ReceiveMessage(user, message);
        }
}

Использование Hub<IChatClient> включает проверку времени компиляции клиентских методов. Это предотвращает проблемы, вызванные использованием магических строк, так как Hub<T> может предоставлять доступ только к методам, определенным в интерфейсе.

Использование строго типизированного Hub<T> отключает возможность использования SendAsync. Все методы, определенные в интерфейсе, по-прежнему могут быть определены как асинхронные. На самом деле, каждый из этих методов должен возвращать Task. Так как это интерфейс, не используйте ключевое async слово. Рассмотрим пример.

public interface IClient
{
    Task ClientMethod();
}

Note

Суффикс Async не удаляется из имени метода. Если метод клиента не определен с помощью .on('MyMethodAsync'), вам не следует использовать MyMethodAsync в качестве имени.

Изменение имени метода хаба

По умолчанию имя метода концентратора сервера — это имя метода .NET. Однако атрибут HubMethodName можно использовать для изменения этого значения по умолчанию и вручную указать имя метода. Клиент должен использовать это имя вместо имени метода .NET при вызове метода:

[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
    return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}

Обработка событий подключения

SignalR API системы Центров предоставляет OnConnectedAsync и OnDisconnectedAsync виртуальные методы для управления и отслеживания соединений. Переопределите виртуальный OnConnectedAsync метод для выполнения действий, когда клиент подключается к Центру, например добавление его в группу:

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Переопределите виртуальный OnDisconnectedAsync метод для выполнения действий при отключении клиента. Если клиент намеренно отключается (например, вызывая connection.stop()), параметр exception станет null. Однако если клиент отключен из-за ошибки (например, сбой сети), exception параметр будет содержать исключение, описывающее сбой:

public override async Task OnDisconnectedAsync(Exception exception)
{
    await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
    await base.OnDisconnectedAsync(exception);
}

RemoveFromGroupAsync не нужно вызывать в OnDisconnectedAsync, это обрабатывается автоматически.

Warning

Предупреждение безопасности: предоставление ConnectionId доступа может привести к вредоносному олицетворению, если SignalR сервер или версия клиента ASP.NET Core 2.2 или более ранней.

Управление ошибками

Исключения, возникающие в методах хаба, отправляются клиенту, который вызвал метод. В клиенте JavaScript метод invoke возвращает JavaScript Promise. Когда клиент получает ошибку с обработчиком, прикрепленным к обещанию, обработчик catch вызывается и ему передается объект JavaScript Error.

connection.invoke("SendMessage", user, message).catch(err => console.error(err));

Если центр создает исключение, подключения не закрываются. По умолчанию SignalR возвращается универсальное сообщение об ошибке клиенту. Рассмотрим пример.

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.

Непредвиденные исключения часто содержат конфиденциальную информацию, например, имя сервера базы данных в случае, если соединение с базой данных прерывается. SignalR не предоставляет эти подробные сообщения об ошибках по умолчанию в качестве меры безопасности. Дополнительные сведения о том, почему сведения об исключении подавляются, см.: в разделе "Вопросы безопасности" в ASP.NET Core SignalR.

Если у вас есть исключительное условие, которое вы хотите распространить на клиент, можно использовать HubException класс. Если вы выбрасываете HubException из метода концентратора, SignalRотправит неизменённое сообщение клиенту:

public Task ThrowException()
{
    throw new HubException("This error will be sent to the client!");
}

Note

SignalR отправляет клиенту только свойство Message исключения. Трассировка стека и другие свойства исключения недоступны клиенту.

Дополнительные ресурсы