Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
SignalR API Hubs позволяет подключенным клиентам вызывать методы на сервере, обеспечивая обмен данными в режиме реального времени. Сервер определяет методы, вызываемые клиентом, и клиент определяет методы, вызываемые сервером. SignalR также обеспечивает непрямое взаимодействие между клиентами, где SignalR Hub выступает посредником. Такой подход позволяет отправлять сообщения между отдельными клиентами, группами или всеми подключенными клиентами. 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.
Note
Параметры метода хаба, возвращаемые значения и элементы потока могут быть типами объединения C# только при использовании JsonHubProtocol по умолчанию. Протоколы MessagePack и Newtonsoft.Json hub не поддерживают объединения.
Использование свойств и методов объекта 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);
Внедрение служб в концентратор
Конструкторы хабов могут принимать службы через механизм внедрения зависимостей в качестве параметров; эти службы можно сохранять в свойствах класса для использования в методе хаба.
Когда вы внедряете несколько служб для разных методов хаба или используете это как альтернативный способ написания кода, методы хаба также могут получать службы через внедрение зависимостей. По умолчанию параметры метода Hub анализируются и, если это возможно, получают через механизм внедрения зависимостей.
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 .
Чтобы явно указать, какие параметры в методах хаба получают значения через внедрение зависимостей, используйте свойство DisableImplicitFromServicesParameters. Укажите для параметров метода концентратора атрибут [FromServices] или пользовательский атрибут, реализующий IFromServiceMetadata, которые должны разрешаться через внедрение зависимостей.
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функцию, которая при необходимости реализуется в конфигурациях внедрения зависимостей. Если контейнер внедрения зависимостей приложения не поддерживает эту функцию, внедрение служб в методы концентратора не поддерживается.
Поддержка ключевых служб при внедрении зависимостей
Механизм служб с ключами позволяет регистрировать и получать службы внедрения зависимостей с помощью ключей. Служба связана с ключом, вызывая AddKeyedSingleton метод для его регистрации. В качестве альтернативы можно вызвать AddKeyedScoped или AddKeyedTransient метод.
Чтобы получить доступ к зарегистрированной службе, укажите ключ с атрибутом [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"));
}
}
Ограничение вызовов потоковой передачи для каждого подключения
MaximumParallelInvocationsPerClient определяет количество вызовов методов концентратора без потоковой передачи, которые клиент может выполнять параллельно, прежде чем они будут поставлены в очередь. Он не применяется к вызовам концентратора потоковой передачи. Вызовы потоковой передачи намеренно исключаются из-за того, что они, как ожидается, будут длительными и параллельными, поэтому клиент может запускать любое количество одновременных потоков независимо от этого параметра.
Чтобы применить ограничение на число вызовов потоковой передачи для каждого подключения, оберните поток в самом методе хаба с помощью закрытого вспомогательного метода, который увеличивает счётчик перед выдачей элементов и уменьшает его в блоке finally:
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
public class StreamingHub : Hub
{
private static readonly ConcurrentDictionary<string, int> _activeStreams = new();
private const int MaxConcurrentStreams = 2;
public IAsyncEnumerable<int> Counter(
int count,
int delay,
CancellationToken cancellationToken)
{
return WithLimit(Context.ConnectionId, GetCounter(count, delay, cancellationToken));
}
private async IAsyncEnumerable<int> GetCounter(
int count,
int delay,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
for (var i = 0; i < count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
yield return i;
await Task.Delay(delay, cancellationToken);
}
}
private async IAsyncEnumerable<T> WithLimit(
string connectionId,
IAsyncEnumerable<T> stream,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var current = _activeStreams.AddOrUpdate(
connectionId,
addValue: 1,
updateValueFactory: (_, count) => count + 1);
if (current > MaxConcurrentStreams)
{
Decrement(connectionId);
throw new HubException(
$"The connection is limited to {MaxConcurrentStreams} concurrent streaming invocations.");
}
try
{
await foreach (var item in stream.WithCancellation(cancellationToken))
{
yield return item;
}
}
finally
{
Decrement(connectionId);
}
}
private static void Decrement(string connectionId)
{
while (_activeStreams.TryGetValue(connectionId, out var current))
{
if (current <= 1)
{
if (_activeStreams.TryRemove(new KeyValuePair<string, int>(connectionId, current)))
{
return;
}
}
else if (_activeStreams.TryUpdate(connectionId, current - 1, current))
{
return;
}
}
}
}
Ключевой момент в том, что WithLimit оборачивает исходный IAsyncEnumerable<T> и поддерживает счётчик увеличенным на протяжении всего времени жизни потока, а не только до тех пор, пока не будет выдан первый элемент.
Блок finally выполняется только тогда, когда клиент завершает чтение потока, отменяет его или соединение разрывается.
Если методы концентратора потоковой передачи возвращают ChannelReader<T> вместо IAsyncEnumerable<T>, можно применить аналогичную обёртку. Он должен использовать один и тот же словарь _activeStreams, чтобы оба типа потоков использовали одно общее ограничение на уровне соединения, а не поддерживали каждый свой отдельный счётчик.
Note
Словарь _activeStreams — static, поэтому он используется совместно всеми экземплярами концентратора. Если вы предпочитаете состояние, управляемое через 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 | Прерывает подключение. |
Объект Клиенты
Класс 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 | Прерывает подключение. |
Объект Клиенты
Класс 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 | Прерывает подключение. |
Объект Клиенты
Класс 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 | Прерывает подключение. |
Объект Клиенты
Класс 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 исключения. Трассировка стека и другие свойства исключения недоступны клиенту.
Дополнительные ресурсы
ASP.NET Core