Клиент JavaScript SignalR ASP.NET Core
Автор: Рэйчел Аппель (Rachel Appel)
Клиентская библиотека JavaScript ASP.NET Core SignalR позволяет разработчикам вызывать код концентратора на стороне SignalR сервера.
Установка клиентского SignalR пакета
Клиентская SignalR библиотека JavaScript поставляется в виде пакета npm . В следующих разделах описаны различные способы установки клиентской библиотеки.
Установка с помощью npm
Выполните следующие команды в консоли диспетчера пакетов:
npm init -y
npm install @microsoft/signalr
npm устанавливает содержимое пакета в папку node_modules\@microsoft\signalr\dist\browser . Создайте папку wwwroot/lib/signalr . signalr.js
Скопируйте файл в папку wwwroot/lib/signalr.
Укажите ссылку на SignalR клиент JavaScript в элементе <script>
. Пример:
<script src="~/lib/signalr/signalr.js"></script>
Использование сети доставки содержимого (CDN)
Чтобы использовать клиентную библиотеку без предварительных требований npm, составьте ссылку на размещенную в CDN копию клиентской библиотеки. Пример:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
Клиентская библиотека доступна в следующих сетях CDN:
Установка с помощью LibMan
LibMan можно использовать для установки определенных файлов клиентской библиотеки из клиентской библиотеки, размещенной в CDN. Например, добавьте в проект только минифицированный файл JavaScript. Дополнительные сведения об этом подходе см. в разделе Добавление клиентской библиотекиSignalR.
Подключение к концентратору
Следующий код создает и запускает подключение. Имя концентратора не учитывает регистр:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
Подключения между источниками (CORS)
Как правило, браузеры загружают подключения из того же домена, что и запрошенная страница. Однако бывают случаи, когда требуется подключение к другому домену.
При выполнении междоменных запросов клиентский код должен использовать абсолютный URL-адрес вместо относительного URL-адреса. Для междоменных запросов измените на .withUrl("/chathub")
.withUrl("https://{App domain name}/chathub")
.
Чтобы вредоносный сайт не считывал конфиденциальные данные с другого сайта, подключения между источниками отключены по умолчанию. Чтобы разрешить запрос независимо от источника, включите CORS:
using SignalRChat.Hubs;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("https://example.com")
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// UseCors must be called before MapHub.
app.UseCors();
app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
UseCors должен вызываться перед вызовом MapHub.
Вызов методов концентратора из клиента
Клиенты JavaScript вызывают открытые методы в концентраторах с помощью метода invokeHubConnection. Метод invoke
принимает:
- Имя метода концентратора.
- Любые аргументы, определенные в методе концентратора.
В следующем выделенном коде имя метода в концентраторе — SendMessage
. Второй и третий аргументы, переданные для invoke
сопоставления с аргументами и message
метода концентратораuser
:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Вызов методов концентратора из клиента поддерживается только при использовании службы Azure SignalR в режиме по умолчанию . Дополнительные сведения см. в разделе Часто задаваемые вопросы (репозиторий GitHub azure-signalr).
Метод invoke
возвращает JavaScript Promise
. разрешается Promise
с возвращаемым значением (если таковое имеется) при возврате метода на сервере. Если метод на сервере выдает ошибку, Promise
объект отклоняется с сообщением об ошибке. Для обработки этих случаев используйте async
методы и then
await
Promise
catch
или .
Клиенты JavaScript также могут вызывать открытые методы в концентраторах с помощью метода send объекта HubConnection
. invoke
В отличие от метода , send
метод не ожидает ответа от сервера. Метод send
возвращает JavaScript Promise
. Разрешается Promise
при отправке сообщения на сервер. При возникновении ошибки при отправке сообщения Promise
объект отклоняется вместе с сообщением об ошибке. Для обработки этих случаев используйте async
методы и then
await
Promise
catch
или .
При использовании send
не ожидается, пока сервер получит сообщение. Следовательно, невозможно вернуть данные или ошибки с сервера.
Вызов клиентских методов из концентратора
Чтобы получать сообщения из концентратора, определите метод с помощью метода HubConnection
on объекта .
- Имя метода клиента JavaScript.
- Аргументы, которые концентратор передает методу .
В следующем примере имя метода — ReceiveMessage
. Имена аргументов: user
и message
:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
Предыдущий код в выполняется connection.on
, когда серверный код вызывает его с помощью SendAsync метода :
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
SignalR определяет метод клиента для вызова путем сопоставления имени метода и аргументов, определенных в SendAsync
и connection.on
.
Рекомендуется вызвать метод start для HubConnection
после on
. Это гарантирует, что обработчики будут зарегистрированы перед получением каких-либо сообщений.
Обработка ошибок и ведение журнала
Используйте для console.error
вывода ошибок в консоль браузера, когда клиенту не удается подключиться или отправить сообщение:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Настройте трассировку журналов на стороне клиента, передав средство ведения журнала и тип события в журнал при подключении. Сообщения регистрируются с указанным уровнем журнала и выше. Ниже приведены доступные уровни ведения журнала.
signalR.LogLevel.Error
: сообщения об ошибках.Error
Регистрирует только сообщения.signalR.LogLevel.Warning
: предупреждающие сообщения о потенциальных ошибках.Warning
Регистрирует сообщения , иError
.signalR.LogLevel.Information
: сообщения о состоянии без ошибок.Information
Регистрирует сообщения ,Warning
иError
.signalR.LogLevel.Trace
: сообщения трассировки. Регистрирует все данные, включая данные, передаваемые между концентратором и клиентом.
Для настройки уровня журнала используйте метод configureLogging в HubConnectionBuilder . Сообщения записываются в консоль браузера:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Повторное подключение клиентов
Автоматическое повторное подключение
Клиент JavaScript для SignalR можно настроить для автоматического повторного подключения с помощью метода WithAutomaticReconnect в HubConnectionBuilder. По умолчанию повторное подключение не выполняется автоматически.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Без параметров WithAutomaticReconnect настраивает клиент на ожидание 0, 2, 10 и 30 секунд, прежде чем пытаться повторно подключиться. После четырех неудачных попыток повторное подключение прекращается.
Перед началом любых попыток повторного подключения выполните :HubConnection
- Переходит в
HubConnectionState.Reconnecting
состояние и запускает егоonreconnecting
обратные вызовы. - Не переходит в
Disconnected
состояние и не активирует обратныеonclose
вызовы, как без настройки автоматическогоHubConnection
повторного подключения.
Подход к повторному подключению обеспечивает следующие возможности:
- Предупреждайте пользователей о том, что подключение было потеряно.
- Отключите элементы пользовательского интерфейса.
connection.onreconnecting(error => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messageList").appendChild(li);
});
Если клиент успешно повторно подключается в течение первых четырех попыток, HubConnection
он возвращается в Connected
состояние и запускает обратные onreconnected
вызовы. Это дает возможность сообщить пользователям о том, что подключение было восстановлено.
Так как подключение выглядит совершенно новым для сервера, для обратного onreconnected
вызова предоставляется новыйconnectionId
.
Параметр onreconnected
обратного connectionId
вызова не определен, если HubConnection
настроен для пропуска согласования.
connection.onreconnected(connectionId => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messageList").appendChild(li);
});
withAutomaticReconnect
не настроит HubConnection
для повторных попыток начального запуска, поэтому сбои запуска должны обрабатываться вручную:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("SignalR Connected.");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
Если клиент не удалось восстановить подключение в течение первых четырех попыток, HubConnection
он переходит в Disconnected
состояние и запускает обратные вызовы onclose . Это дает возможность информировать пользователей:
- Соединение было окончательно потеряно.
- Попробуйте обновить страницу:
connection.onclose(error => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messageList").appendChild(li);
});
Чтобы настроить пользовательское число попыток повторного подключения перед отключением или изменением времени повторного подключения, принимает массив чисел, withAutomaticReconnect
представляющий задержку в миллисекундах, чтобы ждать перед началом каждой попытки повторного подключения.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
В предыдущем примере настраивается HubConnection
, чтобы начать попытку повторного подключения сразу после потери подключения. Конфигурация по умолчанию также ожидает 0 секунд, чтобы попытаться повторно подключиться.
Если первая попытка повторного подключения завершается неудачей, вторая попытка повторного подключения также начинается немедленно, а не в течение 2 секунд с использованием конфигурации по умолчанию.
Если вторая попытка повторного подключения завершается неудачей, третья попытка повторного подключения начинается через 10 секунд, что совпадает с конфигурацией по умолчанию.
Настроенное время повторного подключения отличается от поведения по умолчанию, так как останавливается после третьей попытки повторного подключения, а не пытается повторить попытку повторного подключения еще через 30 секунд.
Для большего контроля над временем и количеством попыток автоматического повторного подключения принимает объект, withAutomaticReconnect
реализующий IRetryPolicy
интерфейс , который имеет один метод с именем nextRetryDelayInMilliseconds
.
nextRetryDelayInMilliseconds
принимает один аргумент с типом RetryContext
. Имеет RetryContext
три свойства: previousRetryCount
и , elapsedMilliseconds
retryReason
которые являются number
, number
и Error
соответственно. Перед первой попыткой повторного подключения оба previousRetryCount
значения и elapsedMilliseconds
будут равны нулю, а значение retryReason
будет ошибкой, которая привела к потере подключения. После каждой неудачной попытки повторных попыток будет увеличиваться на единицу, elapsedMilliseconds
будет обновляться с учетом времени, previousRetryCount
затраченного на повторное подключение до сих пор в миллисекундах, а retryReason
значение будет ошибкой, которая привела к сбою последней попытки повторного подключения.
nextRetryDelayInMilliseconds
должен возвращать число, представляющее число миллисекунда для ожидания перед следующей попыткой повторного подключения, или null
значение , если HubConnection
повторное подключение должно прекратиться.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
Кроме того, можно написать код, который повторно подключает клиент вручную, как показано в следующем разделе.
Повторное подключение вручную
В следующем коде демонстрируется типичный подход к повторному подключению вручную:
- Для запуска соединения создается функция (в данном случае
start
функция). - Вызовите функцию
start
в обработчикеonclose
событий соединения.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
В производственных реализациях обычно используется экспоненциальная обратная попытка или повторение указанного числа раз.
Вкладка "Спящий режим" браузера
Некоторые браузеры имеют функцию зависания вкладок или спящего режиме, чтобы сократить использование компьютерных ресурсов для неактивных вкладок. Это может привести SignalR к закрытию подключений и нежелательному взаимодействию с пользователем. Браузеры используют эвристические средства, чтобы выяснить, следует ли переходить вкладку в спящий режим, например:
- Воспроизведение звука
- Удержание веб-блокировки
- Удержание
IndexedDB
блокировки - Подключение к USB-устройству
- Запись видео или звука
- Зеркальное отображение
- Захват окна или дисплея
Эвристика браузера может меняться со временем и может отличаться в разных браузерах. Просмотрите матрицу поддержки и выясните, какой метод лучше всего подходит для ваших сценариев.
Чтобы избежать перевода приложения в спящий режим, приложение должно активировать одну из эвристических мер, которые использует браузер.
В следующем примере кода показано, как использовать веб-блокировку для поддержания состояния вкладки и предотвращения непредвиденного закрытия подключения.
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
const promise = new Promise((res) => {
lockResolver = res;
});
navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
return promise;
});
}
В приведенном выше примере кода:
- Веб-блокировки являются экспериментальными. Условный проверка подтверждает, что браузер поддерживает веб-блокировки.
- Сопоставитель
lockResolver
обещаний хранится, чтобы можно было освободить блокировку, когда вкладка будет находиться в спящем режиме. - При закрытии подключения блокировка освобождается путем вызова
lockResolver()
. После снятия блокировки вкладка может находиться в спящем режиме.
Дополнительные ресурсы
- Просмотреть или скачать образец кода (как скачивать)
- Справочник по API JavaScript
- Руководство по JavaScript
- Руководство по WebPack и TypeScript
- Концентраторы
- Клиент .NET
- Публикация в Azure
- Запросы независимо от источника (CORS)
- Документация по бессерверным службам Azure SignalR
- Устранение ошибок подключения
Автор: Рэйчел Аппель (Rachel Appel)
Клиентская библиотека JavaScript ASP.NET Core SignalR позволяет разработчикам вызывать код концентратора на стороне сервера.
Просмотреть или скачать образец кода (как скачивать)
Установка клиентского SignalR пакета
Клиентская SignalR библиотека JavaScript поставляется в виде пакета npm . В следующих разделах описаны различные способы установки клиентской библиотеки.
Установка с помощью npm
Для Visual Studio выполните следующие команды в консоли диспетчера пакетов , находясь в корневой папке. Для Visual Studio Code выполните следующие команды в интегрированном терминале.
npm init -y
npm install @microsoft/signalr
npm устанавливает содержимое пакета в папку node_modules\@microsoft\signalr\dist\browser . Создайте папку с именем signalr в папке wwwroot\lib . signalr.js
Скопируйте файл в папку wwwroot\lib\signalr.
Укажите ссылку на SignalR клиент JavaScript в элементе <script>
. Пример:
<script src="~/lib/signalr/signalr.js"></script>
Использование сети доставки содержимого (CDN)
Чтобы использовать клиентную библиотеку без предварительных требований npm, составьте ссылку на размещенную в CDN копию клиентской библиотеки. Пример:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.js"></script>
Клиентская библиотека доступна в следующих сетях CDN:
Установка с помощью LibMan
LibMan можно использовать для установки определенных файлов клиентской библиотеки из клиентской библиотеки, размещенной в CDN. Например, добавьте в проект только минифицированный файл JavaScript. Дополнительные сведения об этом подходе см. в разделе Добавление клиентской библиотекиSignalR.
Подключение к концентратору
Следующий код создает и запускает подключение. Имя концентратора не учитывает регистр:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
Подключения между источниками
Как правило, браузеры загружают подключения из того же домена, что и запрошенная страница. Однако бывают случаи, когда требуется подключение к другому домену.
Важно!
Клиентский код должен использовать абсолютный URL-адрес вместо относительного URL-адреса. Измените .withUrl("/chathub")
на .withUrl("https://myappurl/chathub")
.
Чтобы вредоносный сайт не считывал конфиденциальные данные с другого сайта, подключения между источниками отключены по умолчанию. Чтобы разрешить запрос независимо от источника, включите его в Startup
классе :
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalRChat.Hubs;
namespace SignalRChat
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("https://example.com")
.AllowCredentials();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
}
Вызов методов концентратора из клиента
Клиенты JavaScript вызывают открытые методы в концентраторах с помощью метода invokeHubConnection. Метод invoke
принимает:
- Имя метода концентратора.
- Любые аргументы, определенные в методе концентратора.
В следующем примере имя метода в концентраторе — SendMessage
. Второй и третий аргументы, переданные для invoke
сопоставления с аргументами и message
метода концентратораuser
:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Примечание
Вызов методов концентратора из клиента поддерживается только при использовании службы Azure SignalR в режиме по умолчанию . Дополнительные сведения см. в разделе Часто задаваемые вопросы (репозиторий GitHub azure-signalr).
Метод invoke
возвращает JavaScript Promise
. разрешается Promise
с возвращаемым значением (если таковое имеется) при возврате метода на сервере. Если метод на сервере выдает ошибку, Promise
объект отклоняется с сообщением об ошибке. Для обработки этих случаев используйте async
методы и then
await
Promise
catch
или .
Клиенты JavaScript также могут вызывать открытые методы в концентраторах с помощью метода send объекта HubConnection
. invoke
В отличие от метода , send
метод не ожидает ответа от сервера. Метод send
возвращает JavaScript Promise
. Разрешается Promise
при отправке сообщения на сервер. При возникновении ошибки при отправке сообщения Promise
объект отклоняется вместе с сообщением об ошибке. Для обработки этих случаев используйте async
методы и then
await
Promise
catch
или .
Примечание
При использовании send
не ожидается, пока сервер получит сообщение. Следовательно, невозможно вернуть данные или ошибки с сервера.
Вызов клиентских методов из концентратора
Чтобы получать сообщения из концентратора, определите метод с помощью метода HubConnection
on объекта .
- Имя метода клиента JavaScript.
- Аргументы, которые концентратор передает методу .
В следующем примере имя метода — ReceiveMessage
. Имена аргументов: user
и message
:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
Предыдущий код в выполняется connection.on
, когда серверный код вызывает его с помощью SendAsync метода :
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
SignalR определяет метод клиента для вызова путем сопоставления имени метода и аргументов, определенных в SendAsync
и connection.on
.
Примечание
Рекомендуется вызвать метод start в HubConnection
после on
. Это гарантирует, что обработчики будут зарегистрированы перед получением сообщений.
Обработка ошибок и ведение журнала
Используйте try
и catch
с async
методом Promise
catch
и await
или для обработки ошибок на стороне клиента. Используйте console.error
для вывода ошибок в консоль браузера:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Настройте трассировку журналов на стороне клиента, передав средство ведения журнала и тип события в журнал при подключении. Сообщения регистрируются с указанным уровнем журнала и выше. Ниже приведены доступные уровни ведения журнала.
signalR.LogLevel.Error
: сообщения об ошибках.Error
Регистрирует только сообщения.signalR.LogLevel.Warning
: предупреждающие сообщения о потенциальных ошибках.Warning
Регистрирует сообщения , иError
.signalR.LogLevel.Information
: сообщения о состоянии без ошибок.Information
Регистрирует сообщения ,Warning
иError
.signalR.LogLevel.Trace
: сообщения трассировки. Регистрирует все данные, включая данные, передаваемые между концентратором и клиентом.
Для настройки уровня журнала используйте метод configureLogging в HubConnectionBuilder . Сообщения записываются в консоль браузера:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Повторное подключение клиентов
Автоматическое повторное подключение
Клиент JavaScript для SignalR можно настроить для автоматического повторного withAutomaticReconnect
подключения с помощью метода HubConnectionBuilder. По умолчанию повторное подключение не выполняется автоматически.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Без каких-либо параметров withAutomaticReconnect()
настраивает клиент на ожидание 0, 2, 10 и 30 секунд, прежде чем пытаться повторно подключиться, и останавливается после четырех неудачных попыток.
Перед началом попыток повторного HubConnection
подключения объект перейдет HubConnectionState.Reconnecting
в состояние и запустит обратные onreconnecting
вызовы вместо перехода Disconnected
в состояние и активации обратных onclose
вызовов, например HubConnection
, без настройки автоматического повторного подключения. Это позволяет предупреждать пользователей о потере подключения и отключать элементы пользовательского интерфейса.
connection.onreconnecting(error => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messageList").appendChild(li);
});
Если клиент успешно повторно подключается в течение первых четырех попыток, HubConnection
объект переключится обратно в Connected
состояние и запустит обратные onreconnected
вызовы. Это дает возможность сообщить пользователям о том, что подключение было восстановлено.
Так как подключение выглядит совершенно новым для сервера, для обратного onreconnected
вызова будет предоставлен новыйconnectionId
.
Предупреждение
Параметр onreconnected
обратного connectionId
вызова будет не определен, если HubConnection
параметр был настроен для пропуска согласования.
connection.onreconnected(connectionId => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messageList").appendChild(li);
});
withAutomaticReconnect()
не настроит HubConnection
для повторных попыток начального запуска, поэтому сбои запуска должны обрабатываться вручную:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("SignalR Connected.");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
Если клиенту не удалось выполнить повторное подключение в течение первых четырех попыток, HubConnection
объект перейдет в Disconnected
состояние и запустит обратные вызовы onclose . Это дает возможность сообщить пользователям о том, что подключение было окончательно потеряно, и рекомендовать обновить страницу:
connection.onclose(error => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messageList").appendChild(li);
});
Чтобы настроить пользовательское число попыток повторного подключения перед отключением или изменением времени повторного подключения, принимает массив чисел, withAutomaticReconnect
представляющий задержку в миллисекундах, чтобы ждать перед началом каждой попытки повторного подключения.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
В предыдущем примере настраивается HubConnection
, чтобы начать попытку повторного подключения сразу после потери подключения. Это также относится к конфигурации по умолчанию.
Если первая попытка повторного подключения завершается неудачей, вторая попытка повторного подключения также начнется немедленно, а не будет ждать 2 секунды, как в конфигурации по умолчанию.
Если вторая попытка повторного подключения завершается неудачей, третья попытка повторного подключения начнется через 10 секунд, что снова похоже на конфигурацию по умолчанию.
Затем настраиваемое поведение снова расходится с поведением по умолчанию, останавливаясь после третьего сбоя попытки повторного подключения вместо того, чтобы пытаться повторить попытку повторного подключения еще через 30 секунд, как в конфигурации по умолчанию.
Если требуется еще больше контроля над временем и количеством попыток автоматического повторного подключения, принимает объект, withAutomaticReconnect
реализующий IRetryPolicy
интерфейс , который имеет один метод с именем nextRetryDelayInMilliseconds
.
nextRetryDelayInMilliseconds
принимает один аргумент с типом RetryContext
. Имеет RetryContext
три свойства: previousRetryCount
и , elapsedMilliseconds
retryReason
которые являются number
, number
и Error
соответственно. Перед первой попыткой повторного подключения оба previousRetryCount
значения и elapsedMilliseconds
будут равны нулю, а значение retryReason
будет ошибкой, которая привела к потере подключения. После каждой неудачной попытки повторных попыток будет увеличиваться на единицу, elapsedMilliseconds
будет обновляться с учетом времени, previousRetryCount
затраченного на повторное подключение до сих пор в миллисекундах, а retryReason
значение будет ошибкой, которая привела к сбою последней попытки повторного подключения.
nextRetryDelayInMilliseconds
должен возвращать число, представляющее число миллисекунда для ожидания перед следующей попыткой повторного подключения, или null
значение , если HubConnection
повторное подключение должно прекратиться.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
Кроме того, можно написать код, который будет повторно подключать клиент вручную, как показано в разделе Повторное подключение вручную.
Повторное подключение вручную
В следующем коде демонстрируется типичный подход к повторному подключению вручную:
- Для запуска соединения создается функция (в данном случае
start
функция). - Вызовите функцию
start
в обработчикеonclose
событий соединения.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
В производственных реализациях обычно используется экспоненциальная обратная попытка или повторение указанного числа раз.
Вкладка "Спящий режим" браузера
Некоторые браузеры имеют функцию зависания вкладок или спящего режиме, чтобы сократить использование компьютерных ресурсов для неактивных вкладок. Это может привести SignalR к закрытию подключений и нежелательному взаимодействию с пользователем. Браузеры используют эвристические средства, чтобы выяснить, следует ли переходить вкладку в спящий режим, например:
- Воспроизведение звука
- Удержание веб-блокировки
- Удержание
IndexedDB
блокировки - Подключение к USB-устройству
- Запись видео или звука
- Зеркальное отображение
- Захват окна или дисплея
Примечание
Эти эвристики могут меняться со временем или отличаться в разных браузерах. Проверьте матрицу поддержки и выясните, какой метод лучше всего подходит для ваших сценариев.
Чтобы избежать перевода приложения в спящий режим, приложение должно активировать одну из эвристических мер, которые использует браузер.
В следующем примере кода показано, как использовать веб-блокировку для поддержания состояния вкладки и предотвращения непредвиденного закрытия подключения.
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
const promise = new Promise((res) => {
lockResolver = res;
});
navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
return promise;
});
}
В приведенном выше примере кода:
- Веб-блокировки являются экспериментальными. Условный проверка подтверждает, что браузер поддерживает веб-блокировки.
- Сопоставитель обещаний (
lockResolver
) сохраняется, чтобы блокировка была снята, когда вкладка будет находиться в спящем режиме. - При закрытии подключения блокировка освобождается путем вызова
lockResolver()
. После снятия блокировки вкладка может находиться в спящем режиме.