Руководство по ASP.NET Core BlazorSignalR

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

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

В текущем выпуске см . версию .NET 8 этой статьи.

В этой статье описывается процесс настройки подключений SignalR в программах Blazor и управления ими.

Общие рекомендации по настройке ядра ASP.NET см. в разделах обзора ASP.NET SignalR основной SignalRобласти документации, особенно ASP.NET конфигурации CoreSignalR.

Серверные приложения используют ASP.NET Core SignalR для взаимодействия с браузером. SignalRУсловия размещения и масштабирования применяются к серверным приложениям.

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

Служба Azure SignalR с повторным подключением с отслеживанием состояния

Повторное подключение с отслеживанием состояния () было выпущено с помощью .NET 8,WithStatefulReconnect но в настоящее время не поддерживается для службы Azure SignalR . Дополнительные сведения см. в разделе "Поддержка повторного подключения с отслеживанием состояния"? (Azure/azure-signalr #1878).

Сжатие WebSocket для компонентов интерактивного сервера

По умолчанию компоненты интерактивного сервера:

frame-ancestors CSP можно удалить вручную, задав значение nullConfigureWebSocketOptions , так как может потребоваться настроить CSP централизованно. frame-ancestors Если CSP управляется централизованно, необходимо принять меры по применению политики всякий раз при отображении первого документа. Мы не рекомендуем полностью удалить политику, так как это может сделать приложение уязвимым для атаки.

Примеры использования:

Отключите сжатие, установив для этого ConfigureWebSocketOptions nullзначение, что снижает уязвимость приложения к атаке , но может привести к снижению производительности:

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)

Если сжатие включено, настройте более frame-ancestors строгий CSP со значением 'none' (обязательными одними кавычками), что позволяет сжатию WebSocket, но не позволяет браузерам внедрять приложение в любое:<iframe>

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")

Если сжатие включено, удалите frame-ancestors CSP, установив для него значение ContentSecurityFrameAncestorsPolicy null. Этот сценарий рекомендуется использовать только для приложений, которые задают CSP централизованно.

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = null)

Внимание

Браузеры применяют директивы CSP из нескольких заголовков CSP с использованием самого строгого значения директивы политики. Поэтому разработчик не может добавить более слабую frame-ancestors политику, чем 'self' по назначению или по ошибке.

Для строкового значения, передаваемого ContentSecurityFrameAncestorsPolicyв :

Неподдерживаемые значения: noneself

'self'

Дополнительные параметры включают указание одного или нескольких источников узлов и источников схемы.

Сведения о последствиях безопасности см. в руководстве по устранению угроз для интерактивной отрисовки ASP.NET Core Blazor на стороне сервера. Дополнительные сведения о директиве frame-ancestors см. в разделе CSP: frame-ancestors (документация по MDN).

Отключение сжатия ответов для Горячей перезагрузки

При использовании Горячей перезагрузки отключите ПО промежуточного слоя для сжатия ответов в среде Development. Независимо от того, используется ли код по умолчанию из шаблона проекта, UseResponseCompression всегда вызывается первым в конвейере обработки запросов.

В файле Program:

if (!app.Environment.IsDevelopment())
{
    app.UseResponseCompression();
}

Согласование между источниками на стороне SignalR клиента для проверки подлинности

В этом разделе объясняется, как настроить SignalRбазовый клиент для отправки учетных данных, таких как файлы cookie или заголовки проверки подлинности HTTP.

используйте SetBrowserRequestCredentials, чтобы задать Include в независящих от источника запросах fetch.

IncludeRequestCredentialsMessageHandler.cs:

using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Http;

public class IncludeRequestCredentialsMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
        return base.SendAsync(request, cancellationToken);
    }
}

Если подключение концентратора установлено, назначьте HttpMessageHandler параметру HttpMessageHandlerFactory:

private HubConnectionBuilder? hubConnection;

...

hubConnection = new HubConnectionBuilder()
    .WithUrl(new Uri(Navigation.ToAbsoluteUri("/chathub")), options =>
    {
        options.HttpMessageHandlerFactory = innerHandler => 
            new IncludeRequestCredentialsMessageHandler { InnerHandler = innerHandler };
    }).Build();

В предыдущем примере url-адрес подключения концентратора настраивается на абсолютный адрес URI по /chathubадресу. Код URI также может быть установлен через строку, например https://signalr.example.com, или через конфигурацию. Navigation является внедренным NavigationManager.

Дополнительные сведения см. в статье Конфигурация ASP.NET Core SignalR.

Отрисовка на стороне клиента

Если настроена предварительная отрисовка, предварительная отрисовка возникает до установки подключения клиента к серверу. Дополнительные сведения см. в разделе "Предварительная ASP.NET Основные Razor компоненты".

Если настроена предварительная отрисовка, предварительная отрисовка возникает до установки подключения клиента к серверу. Дополнительные сведения см. в следующих статьях:

Предопределенный размер состояния и SignalR ограничение размера сообщения

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

  • Не SignalR удается инициализировать канал с ошибкой на клиенте: Circuit host not initialized.
  • Пользовательский интерфейс повторного подключения на клиенте отображается при сбое канала. Восстановление невозможно.

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

  • Уменьшите объем данных, которые вы помещаете в предварительно созданное состояние.
  • SignalR Увеличьте размер сообщения. ПРЕДУПРЕЖДЕНИЕ. Увеличение ограничения может увеличить риск атак типа "отказ в обслуживании" (DoS).

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

Использование сопоставления сеансов (липкие сеансы) для размещения веб-фарм на стороне сервера

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

Следующая ошибка возникает приложением, которое не включило сходство сеансов в веб-фарме:

Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed.

Дополнительные сведения о сопоставлении сеансов с размещением служб приложение Azure см. в статье "Размещение узлов" и развертывание приложений на стороне Blazor сервера ASP.NET Core.

Служба Azure SignalR

Необязательная служба Azure SignalR работает вместе с центром приложения для масштабирования серверного приложения SignalR до большого количества одновременных подключений. Кроме того, глобальный охват службы и ее высокопроизводительные центры обработки данных обеспечивают низкую задержку благодаря доступности в разных регионах.

Служба не требуется для Blazor приложений, размещенных в службе приложение Azure или приложениях контейнеров Azure, но может быть полезно в других средах размещения:

  • Чтобы упростить горизонтальное масштабирование подключения.
  • Обработка глобального распределения.

Дополнительные сведения см. в разделе "Узел" и развертывание приложений на стороне Blazorсервера ASP.NET Core.

Параметры обработчика канала на стороне сервера

Настройте канал с CircuitOptionsпомощью . Просмотр значений по умолчанию в источнике ссылок.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Чтение или установка параметров в Program файле с делегатом AddInteractiveServerComponentsпараметров. Заполнитель {OPTION} представляет параметр, а {VALUE} заполнитель — это значение.

В файле Program:

builder.Services.AddRazorComponents().AddInteractiveServerComponents(options =>
{
    options.{OPTION} = {VALUE};
});

Чтение или установка параметров в Program файле с делегатом AddServerSideBlazorпараметров. Заполнитель {OPTION} представляет параметр, а {VALUE} заполнитель — это значение.

В файле Program:

builder.Services.AddServerSideBlazor(options =>
{
    options.{OPTION} = {VALUE};
});

Чтение или установка параметров в Startup.ConfigureServices делегате AddServerSideBlazorпараметров. Заполнитель {OPTION} представляет параметр, а {VALUE} заполнитель — это значение.

В методе Startup.ConfigureServices в файле Startup.cs:

services.AddServerSideBlazor(options =>
{
    options.{OPTION} = {VALUE};
});

Чтобы настроить HubConnectionContext, используйте HubConnectionContextOptions с AddHubOptions. Просмотрите параметры контекста подключения концентратора по умолчанию в эталонном источнике. Описание вариантов в документации см. в SignalR разделе ASP.NET Конфигурация CoreSignalR. Заполнитель {OPTION} представляет параметр, а {VALUE} заполнитель — это значение.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

В файле Program:

builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddHubOptions(options =>
{
    options.{OPTION} = {VALUE};
});

В файле Program:

builder.Services.AddServerSideBlazor().AddHubOptions(options =>
{
    options.{OPTION} = {VALUE};
});

В методе Startup.ConfigureServices в файле Startup.cs:

services.AddServerSideBlazor().AddHubOptions(options =>
{
    options.{OPTION} = {VALUE};
});

Предупреждение

Значение MaximumReceiveMessageSize по умолчанию — 32 КБ. Увеличение значения может увеличить риск атак типа "отказ в обслуживании" (DoS).

BlazorMaximumParallelInvocationsPerClient использует значение 1, которое является значением по умолчанию. Дополнительные сведения см. в разделе MaximumParallelInvocationsPerClient > 1 разорвает отправку файла в Blazor Server режиме (dotnet/aspnetcore #53951).

Сведения об управлении памятью см. в разделе "Узел" и развертывание приложений на сторонеBlazor сервера ASP.NET Core.

Blazor Параметры концентратора

Настройте MapBlazorHub параметры для управления HttpConnectionDispatcherOptions Blazor центром. Просмотрите параметры диспетчера подключений концентратора по умолчанию в эталонном источнике. Заполнитель {OPTION} представляет параметр, а {VALUE} заполнитель — это значение.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Поместите вызов app.MapBlazorHub после вызова app.MapRazorComponents в файл приложения Program :

app.MapBlazorHub(options =>
{
    options.{OPTION} = {VALUE};
});

Настройка концентратора, используемого AddInteractiveServerRenderMode сбоем с MapBlazorHub помощью AmbiguousMatchException:

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.

Чтобы решить проблему для приложений, предназначенных для .NET 8, предоставьте настраиваемой Blazor WithOrder центру более высокий приоритет с помощью метода:

app.MapBlazorHub(options =>
{
    options.CloseOnAuthenticationExpiration = true;
}).WithOrder(-1);

Дополнительные сведения см. на следующих ресурсах:

Укажите параметры app.MapBlazorHub в файле приложения Program :

app.MapBlazorHub(options =>
{
    options.{OPTION} = {VALUE};
});

Укажите параметры app.MapBlazorHub в конфигурации маршрутизации конечных точек:

app.UseEndpoints(endpoints =>
{
    endpoints.MapBlazorHub(options =>
    {
        options.{OPTION} = {VALUE};
    });
    ...
});

Максимальный размер сообщения получения

Этот раздел применяется только к проектам, реализующим SignalR.

Максимальный размер входящих SignalR сообщений, разрешенных для методов концентратора, ограничен ( HubOptions.MaximumReceiveMessageSize по умолчанию: 32 КБ). SignalR сообщения больше, чем MaximumReceiveMessageSize возникают ошибки. Платформа не накладывает ограничение на размер сообщений SignalR от концентратора клиенту.

Если для ведения журнала SignalR не установлен уровень Отладка или Трассировка, ошибка в связи с недопустимым размером сообщения отображается только в консоли средств разработчика браузера:

Ошибка: подключение отключено с ошибкой "Ошибка: сервер вернул ошибку при закрытии: соединение закрыто с ошибкой."

Если для ведения журнала на стороне сервера SignalR установлен уровень Отладка или Трассировка, функция ведения журнала на стороне сервера предоставляет InvalidDataException для ошибки в связи с недопустимым размером.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      ...
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

Ошибка.

System.IO.InvalidDataException: превышен максимальный размер сообщения 32768B. Размер сообщения можно настроить в AddHubOptions.

Один из подходов включает увеличение предела, задав MaximumReceiveMessageSize в Program файле. В следующем примере максимальный размер сообщения получения составляет 64 КБ:

builder.Services.AddRazorComponents().AddInteractiveServerComponents()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

SignalR Увеличение предельного размера входящих сообщений зависит от затрат на дополнительные ресурсы сервера и повышает риск атак типа "отказ в обслуживании" (DoS). Кроме того, считывание большого объема содержимого в память в виде строк или массивов байтов может привести к неудачным выделениям памяти, которые плохо обрабатываются сборщиком мусора, что дополнительно снизит производительность.

Лучшим вариантом чтения больших полезных данных является отправка содержимого в небольших блоках и обработка полезных данных в виде Stream. Это можно использовать при чтении полезных данных JSON для взаимодействия с JavaScript илиJS при JS наличии необработанных байтов данных взаимодействия. Пример отправки больших двоичных полезных данных в серверных приложениях, использующих методы, аналогичные InputFile компоненту, см . в примере приложения binary Submit и BlazorInputLargeTextArea в примере компонента.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Формы, обрабатывающие большие полезные данные, также могут напрямую использовать взаимодействие потоковой передачи SignalR JS . Дополнительные сведения см. в статье Вызов методов .NET из функций JavaScript в ASP.NET Core Blazor. Пример форм, который передает <textarea> данные на сервер, см. в статье "Устранение неполадок ASP.NET Основных Blazor форм".

Один из подходов включает увеличение предела, задав MaximumReceiveMessageSize в Program файле. В следующем примере максимальный размер сообщения получения составляет 64 КБ:

builder.Services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

SignalR Увеличение предельного размера входящих сообщений зависит от затрат на дополнительные ресурсы сервера и повышает риск атак типа "отказ в обслуживании" (DoS). Кроме того, считывание большого объема содержимого в память в виде строк или массивов байтов может привести к неудачным выделениям памяти, которые плохо обрабатываются сборщиком мусора, что дополнительно снизит производительность.

Лучшим вариантом чтения больших полезных данных является отправка содержимого в небольших блоках и обработка полезных данных в виде Stream. Это можно использовать при чтении полезных данных JSON для взаимодействия с JavaScript илиJS при JS наличии необработанных байтов данных взаимодействия. Пример отправки больших двоичных полезных данных в Blazor Server том, что использует методы, аналогичные InputFile компоненту, см. в примере приложения "Двоичная отправка" иInputLargeTextArea Blazorв примере компонента.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Формы, обрабатывающие большие полезные данные, также могут напрямую использовать взаимодействие потоковой передачи SignalR JS . Дополнительные сведения см. в статье Вызов методов .NET из функций JavaScript в ASP.NET Core Blazor. Пример форм, который передает <textarea> данные в приложении, см. в Blazor Server разделе "Устранение неполадок ASP.NET Основных Blazor форм".

Увеличьте ограничение, настроив MaximumReceiveMessageSize в Startup.ConfigureServices:

services.AddServerSideBlazor()
    .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

SignalR Увеличение предельного размера входящих сообщений зависит от затрат на дополнительные ресурсы сервера и повышает риск атак типа "отказ в обслуживании" (DoS). Кроме того, считывание большого объема содержимого в память в виде строк или массивов байтов может привести к неудачным выделениям памяти, которые плохо обрабатываются сборщиком мусора, что дополнительно снизит производительность.

При разработке кода, который передает большой объем данных, следует учитывать следующее руководство.

  • Используйте встроенную поддержку потокового JS взаимодействия для передачи данных, превышающих SignalR ограничение размера входящих сообщений:
  • Общие советы:
    • Не выделяйте большие объекты в коде C# и JS.
    • Освободите занятую память при завершении или отмене процесса.
    • Примените следующие дополнительные требования для обеспечения безопасности:
      • Объявите максимальный размер файла или данных, который может быть передан.
      • Объявите минимальную скорость передачи от клиента к серверу.
    • После получения данных сервером данные могут быть следующими:
      • Временно сохранены в буфере памяти до тех пор, пока не будут собраны все сегменты.
      • Использованы немедленно. Например, данные могут храниться сразу в базе данных или записываться на диск по мере получения каждого сегмента.
  • Разделите данные на небольшие части и отправляйте сегменты данных последовательно, пока все данные не будут получены сервером.
  • Не выделяйте большие объекты в коде C# и JS.
  • Не блокируйте основной поток пользовательского интерфейса на длительные периоды при отправке или получении данных.
  • Освободите занятую память при завершении или отмене процесса.
  • Примените следующие дополнительные требования для обеспечения безопасности:
    • Объявите максимальный размер файла или данных, который может быть передан.
    • Объявите минимальную скорость передачи от клиента к серверу.
  • После получения данных сервером данные могут быть следующими:
    • Временно сохранены в буфере памяти до тех пор, пока не будут собраны все сегменты.
    • Использованы немедленно. Например, данные могут храниться сразу в базе данных или записываться на диск по мере получения каждого сегмента.

Blazor Конфигурация маршрута конечной точки на стороне сервера

Program В файле вызовите MapBlazorHub сопоставление BlazorHub пути по умолчанию приложения. Скрипт Blazor (blazor.*.js) автоматически указывает на конечную точку, созданную MapBlazorHub.

Отражение состояния подключения на стороне сервера в пользовательском интерфейсе

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

Чтобы настроить пользовательский интерфейс, определите один элемент с id components-reconnect-modal. В следующем примере элемент помещается в App компонент.

App.razor:

Чтобы настроить пользовательский интерфейс, определите один элемент с id components-reconnect-modal. В следующем примере элемент помещает на страницу узла.

Pages/_Host.cshtml:

Чтобы настроить пользовательский интерфейс, определите один элемент с id components-reconnect-modal. В следующем примере элемент помещается на страницу макета.

Pages/_Layout.cshtml:

Чтобы настроить пользовательский интерфейс, определите один элемент с id components-reconnect-modal. В следующем примере элемент помещает на страницу узла.

Pages/_Host.cshtml:

<div id="components-reconnect-modal">
    There was a problem with the connection!
</div>

Примечание.

Если приложение отрисовывает несколько элементов с id components-reconnect-modal, то только первый отрисованный элемент получает изменения класса CSS для отображения или скрытия элемента.

Добавьте следующие стили CSS в таблицу стилей сайта.

wwwroot/app.css:

wwwroot/css/site.css:

#components-reconnect-modal {
    display: none;
}

#components-reconnect-modal.components-reconnect-show, 
#components-reconnect-modal.components-reconnect-failed, 
#components-reconnect-modal.components-reconnect-rejected {
    display: block;
}

В следующей таблице описаны классы CSS, применяемые к элементу components-reconnect-modal платформы Blazor.

Класс CSS Означает...
components-reconnect-show Потеря соединения. Клиент пытается повторно подключиться. Отобразить модальное окно.
components-reconnect-hide Активное подключение к серверу восстановлено. Скрыть модальное окно.
components-reconnect-failed Сбой повторного подключения, возможно, из-за сбоя сети. Чтобы повторить попытку подключения, вызовите метод window.Blazor.reconnect() в JavaScript.
components-reconnect-rejected Повторное подключение отклонено. Сервер был достигнут, но отклонил подключение, и состояние пользователя на сервере потеряно. Чтобы перезагрузить приложение, вызовите метод location.reload() в JavaScript. Это состояние соединения может появиться в следующих случаях:
  • произошел сбой в цепи на стороне сервера;
  • клиент был отключен достаточно долго, чтобы сервер сбросил состояние пользователя. Экземпляры компонентов пользователя удалены.
  • сервер был перезагружен или был выполнен перезапуск рабочего процесса приложения.

Настройте задержку перед появлением пользовательского интерфейса повторного подключения, задав transition-delay свойство в CSS сайта для модального элемента. В следующем примере задержка перехода изменяется с 500 мс (по умолчанию) на 1000 мс (1 секунда).

wwwroot/app.css:

wwwroot/css/site.css:

#components-reconnect-modal {
    transition: visibility 0s linear 1000ms;
}

Чтобы отобразить текущую попытку повторного подключения, определите элемент с параметром id components-reconnect-current-attempt. Чтобы отобразить максимальное число повторных попыток повторного подключения, определите элемент с числом id components-reconnect-max-retriesповторных попыток. В следующем примере эти элементы помещается внутри модального элемента попытки повторного подключения после предыдущего примера.

<div id="components-reconnect-modal">
    There was a problem with the connection!
    (Current reconnect attempt: 
    <span id="components-reconnect-current-attempt"></span> /
    <span id="components-reconnect-max-retries"></span>)
</div>

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

There was a problem with the connection! (Current reconnect attempt: 3 / 8)

Отрисовка на стороне сервера

По умолчанию компоненты предварительно создаются на сервере до установки подключения клиента к серверу. Дополнительные сведения см. в разделе "Предварительная ASP.NET Основные Razor компоненты".

По умолчанию компоненты предварительно создаются на сервере до установки подключения клиента к серверу. Дополнительные сведения см. в статье Вспомогательная функция тега компонента в ASP.NET Core.

Мониторинг активности канала на стороне сервера

Мониторинг активности CreateInboundActivityHandler входящего канала с помощью метода CircuitHandlerв . Действие входящего канала — это любое действие, отправленное из браузера на сервер, например события пользовательского интерфейса или вызовы взаимодействия JavaScript-to-.NET.

Например, можно использовать обработчик действия канала для обнаружения простоя клиента и регистрации его идентификатора канала (Circuit.Id):

using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.Options;
using Timer = System.Timers.Timer;

public sealed class IdleCircuitHandler : CircuitHandler, IDisposable
{
    private Circuit? currentCircuit;
    private readonly ILogger logger;
    private readonly Timer timer;

    public IdleCircuitHandler(ILogger<IdleCircuitHandler> logger, 
        IOptions<IdleCircuitOptions> options)
    {
        timer = new Timer
        {
            Interval = options.Value.IdleTimeout.TotalMilliseconds,
            AutoReset = false
        };

        timer.Elapsed += CircuitIdle;
        this.logger = logger;
    }

    private void CircuitIdle(object? sender, System.Timers.ElapsedEventArgs e)
    {
        logger.LogInformation("{CircuitId} is idle", currentCircuit?.Id);
    }

    public override Task OnCircuitOpenedAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        currentCircuit = circuit;

        return Task.CompletedTask;
    }

    public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
        Func<CircuitInboundActivityContext, Task> next)
    {
        return context =>
        {
            timer.Stop();
            timer.Start();

            return next(context);
        };
    }

    public void Dispose() => timer.Dispose();
}

public class IdleCircuitOptions
{
    public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(5);
}

public static class IdleCircuitHandlerServiceCollectionExtensions
{
    public static IServiceCollection AddIdleCircuitHandler(
        this IServiceCollection services, 
        Action<IdleCircuitOptions> configureOptions)
    {
        services.Configure(configureOptions);
        services.AddIdleCircuitHandler();

        return services;
    }

    public static IServiceCollection AddIdleCircuitHandler(
        this IServiceCollection services)
    {
        services.AddScoped<CircuitHandler, IdleCircuitHandler>();

        return services;
    }
}

Зарегистрируйте службу в Program файле. В следующем примере настраивается время ожидания простоя по умолчанию в течение пяти минут до пяти секунд, чтобы проверить предыдущую IdleCircuitHandler реализацию:

builder.Services.AddIdleCircuitHandler(options => 
    options.IdleTimeout = TimeSpan.FromSeconds(5));

Обработчики действий канала также предоставляют подход для доступа к службам с областью Blazor действия из другихBlazor областей внедрения зависимостей (DI). Дополнительные сведения и примеры см. в следующих примерах:

Запуск Blazor

Настройте ручной запуск Blazor канала приложения SignalR в App.razor файле файла:Blazor Web App

Настройте ручной запуск Blazor канала приложения SignalR в Pages/_Host.cshtml файле (Blazor Server):

Настройте ручной запуск Blazor канала приложения SignalR в Pages/_Layout.cshtml файле (Blazor Server):

Настройте ручной запуск Blazor канала приложения SignalR в Pages/_Host.cshtml файле (Blazor Server):

  • добавьте атрибут autostart="false" в тег <script> для сценария blazor.*.js;
  • Поместите скрипт, который вызывается Blazor.start() после Blazor загрузки скрипта и внутри закрывающего </body> тега.

При отключении autostart все части приложения, которые не зависят от канала, работают нормально. Например, работает маршрутизация на стороне клиента. Однако все аспекты, зависящие от канала, не будут работать до вызова Blazor.start(). Пока не будет установлен канал, поведение приложения будет непредсказуемым. Например, когда канал отключен, методы компонентов не выполняются.

Дополнительные сведения о том, как инициализироватьBlazor, когда документ готов и как приступить к JS Promiseцепочке, см. в разделе ASP.NET Запуск CoreBlazor.

Настройка SignalR времени ожидания и поддержания активности на клиенте

Настройте следующие значения для клиента:

  • withServerTimeout: настраивает время ожидания сервера в миллисекундах. Если это время ожидания истекает без получения сообщений с сервера, подключение завершается ошибкой. По умолчанию время ожидания составляет 30 секунд. Время ожидания сервера должно быть по крайней мере двойным значением, назначенным интервалу keep-Alive (withKeepAliveInterval).
  • withKeepAliveInterval: настраивает интервал "Сохранить в живых" в миллисекундах (интервал по умолчанию для проверки связи с сервером). Этот параметр позволяет серверу обнаруживать жесткие отключения, например, когда клиент отключает компьютер из сети. Связь выполняется чаще всего, как и серверная связь. Если сервер выполняет связь каждые пять секунд, присваивая значение ниже 5000 (5 секунд) пингов каждые пять секунд. Значение по умолчанию — 15 секунд. Интервал "Сохранить в живых" должен быть меньше или равен половине значения, назначенного времени ожидания сервера (withServerTimeout).

В следующем примере файла App.razor (Blazor Web App) показано назначение значений по умолчанию.

Blazor Web App:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      configureSignalR: function (builder) {
        builder.withServerTimeout(30000).withKeepAliveInterval(15000);
      }
    }
  });
</script>

Следующий пример файла Pages/_Host.cshtml (Blazor Serverвсе версии, кроме ASP.NET Core в .NET 6) или Pages/_Layout.cshtml файле (Blazor ServerASP.NET Core в .NET 6).

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      builder.withServerTimeout(30000).withKeepAliveInterval(15000);
    }
  });
</script>

В предыдущем примере {BLAZOR SCRIPT} заполнитель — это путь к скрипту Blazor и имя файла. Сведения о расположении скрипта и пути к использованию см. в разделе ASP.NET Структура проекта CoreBlazor.

При создании подключения концентратора в компоненте установите ServerTimeout (по умолчанию: 30 секунд) и KeepAliveInterval (по умолчанию: 15 секунд) в компоненте HubConnectionBuilder. HandshakeTimeout Установите (по умолчанию: 15 секунд) на встроенном HubConnectionобъекте. В следующем примере показано назначение значений по умолчанию:

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .WithServerTimeout(TimeSpan.FromSeconds(30))
        .WithKeepAliveInterval(TimeSpan.FromSeconds(15))
        .Build();

    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

Настройте следующие значения для клиента:

  • serverTimeoutInMilliseconds: время ожидания сервера в миллисекундах. Если это время ожидания истекает без получения сообщений с сервера, подключение завершается ошибкой. По умолчанию время ожидания составляет 30 секунд. Время ожидания сервера должно быть по крайней мере двойным значением, назначенным интервалу keep-Alive (keepAliveIntervalInMilliseconds).
  • keepAliveIntervalInMilliseconds: интервал по умолчанию, по которому выполняется проверка доступа к серверу. Этот параметр позволяет серверу обнаруживать жесткие отключения, например, когда клиент отключает компьютер из сети. Связь выполняется чаще всего, как и серверная связь. Если сервер выполняет связь каждые пять секунд, присваивая значение ниже 5000 (5 секунд) пингов каждые пять секунд. Значение по умолчанию — 15 секунд. Интервал "Сохранить в живых" должен быть меньше или равен половине значения, назначенного времени ожидания сервера (serverTimeoutInMilliseconds).

Следующий пример файла Pages/_Host.cshtml (Blazor Serverвсе версии, кроме ASP.NET Core в .NET 6) или Pages/_Layout.cshtml файле (Blazor ServerASP.NET Core в .NET 6):

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      let c = builder.build();
      c.serverTimeoutInMilliseconds = 30000;
      c.keepAliveIntervalInMilliseconds = 15000;
      builder.build = () => {
        return c;
      };
    }
  });
</script>

В предыдущем примере {BLAZOR SCRIPT} заполнитель — это путь к скрипту Blazor и имя файла. Сведения о расположении скрипта и пути к использованию см. в разделе ASP.NET Структура проекта CoreBlazor.

При создании подключения концентратора в компоненте установите ServerTimeout (по умолчанию: 30 секунд), HandshakeTimeout (по умолчанию: 15 секунд) и KeepAliveInterval (по умолчанию: 15 секунд) на встроенном HubConnectionобъекте. В следующем примере показано назначение значений по умолчанию:

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .Build();

    hubConnection.ServerTimeout = TimeSpan.FromSeconds(30);
    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);
    hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(15);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

При изменении значений времени ожидания сервера (ServerTimeout) или интервала "Сохранить в живых" (KeepAliveInterval):

  • Время ожидания сервера должно быть по крайней мере двойным значением, назначенным интервалу keep-Alive.
  • Интервал "Сохранить в режиме жизни" должен быть меньше или равен половине значения, назначенного времени ожидания сервера.

Дополнительные сведения см. в разделах "Глобальные сбои развертывания и подключения" в следующих статьях:

Изменение обработчика повторного подключения на стороне сервера

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

  • уведомлять пользователя, если подключение было разорвано;.
  • вносить записи в журнал (со стороны клиента) при подключении канала.

Чтобы изменить события соединения, зарегистрируйте обратные вызовы для следующих изменений соединения:

  • для прерванных соединений используется onConnectionDown;
  • для устанавливаемых и повторно устанавливаемых соединений используется onConnectionUp.

Необходимо задать как onConnectionDown, так и onConnectionUp.

Blazor Web App:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      reconnectionHandler: {
        onConnectionDown: (options, error) => console.error(error),
        onConnectionUp: () => console.log("Up, up, and away!")
      }
    }
  });
</script>

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    reconnectionHandler: {
      onConnectionDown: (options, error) => console.error(error),
      onConnectionUp: () => console.log("Up, up, and away!")
    }
  });
</script>

В предыдущем примере {BLAZOR SCRIPT} заполнитель — это путь к скрипту Blazor и имя файла. Сведения о расположении скрипта и пути к использованию см. в разделе ASP.NET Структура проекта CoreBlazor.

Автоматическое обновление страницы при сбое повторного подключения на стороне сервера

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

App.razor:

Pages/_Host.cshtml:

<div id="reconnect-modal" style="display: none;"></div>
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script src="boot.js"></script>

В предыдущем примере {BLAZOR SCRIPT} заполнитель — это путь к скрипту Blazor и имя файла. Сведения о расположении скрипта и пути к использованию см. в разделе ASP.NET Структура проекта CoreBlazor.

Создайте следующий wwwroot/boot.js файл.

Blazor Web App:

(() => {
  const maximumRetryCount = 3;
  const retryIntervalMilliseconds = 5000;
  const reconnectModal = document.getElementById('reconnect-modal');

  const startReconnectionProcess = () => {
    reconnectModal.style.display = 'block';

    let isCanceled = false;

    (async () => {
      for (let i = 0; i < maximumRetryCount; i++) {
        reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;

        await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));

        if (isCanceled) {
          return;
        }

        try {
          const result = await Blazor.reconnect();
          if (!result) {
            // The server was reached, but the connection was rejected; reload the page.
            location.reload();
            return;
          }

          // Successfully reconnected to the server.
          return;
        } catch {
          // Didn't reach the server; try again.
        }
      }

      // Retried too many times; reload the page.
      location.reload();
    })();

    return {
      cancel: () => {
        isCanceled = true;
        reconnectModal.style.display = 'none';
      },
    };
  };

  let currentReconnectionProcess = null;

  Blazor.start({
    circuit: {
      reconnectionHandler: {
        onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
        onConnectionUp: () => {
          currentReconnectionProcess?.cancel();
          currentReconnectionProcess = null;
        }
      }
    }
  });
})();

Blazor Server:

(() => {
  const maximumRetryCount = 3;
  const retryIntervalMilliseconds = 5000;
  const reconnectModal = document.getElementById('reconnect-modal');

  const startReconnectionProcess = () => {
    reconnectModal.style.display = 'block';

    let isCanceled = false;

    (async () => {
      for (let i = 0; i < maximumRetryCount; i++) {
        reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;

        await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));

        if (isCanceled) {
          return;
        }

        try {
          const result = await Blazor.reconnect();
          if (!result) {
            // The server was reached, but the connection was rejected; reload the page.
            location.reload();
            return;
          }

          // Successfully reconnected to the server.
          return;
        } catch {
          // Didn't reach the server; try again.
        }
      }

      // Retried too many times; reload the page.
      location.reload();
    })();

    return {
      cancel: () => {
        isCanceled = true;
        reconnectModal.style.display = 'none';
      },
    };
  };

  let currentReconnectionProcess = null;

  Blazor.start({
    reconnectionHandler: {
      onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
      onConnectionUp: () => {
        currentReconnectionProcess?.cancel();
        currentReconnectionProcess = null;
      }
    }
  });
})();

Дополнительные сведения о запуске Blazor см. в статье Запуск ASP.NET Core Blazor.

Настройка счетчика повторных попыток и интервала повторного подключения на стороне сервера

Чтобы настроить число попыток и интервал повторного подключения, задайте число повторных попыток (maxRetries) и периодичность в миллисекундах для каждой повторной попытки (retryIntervalMilliseconds).

Blazor Web App:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      reconnectionOptions: {
        maxRetries: 3,
        retryIntervalMilliseconds: 2000
      }
    }
  });
</script>

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    reconnectionOptions: {
      maxRetries: 3,
      retryIntervalMilliseconds: 2000
    }
  });
</script>

В предыдущем примере {BLAZOR SCRIPT} заполнитель — это путь к скрипту Blazor и имя файла. Сведения о расположении скрипта и пути к использованию см. в разделе ASP.NET Структура проекта CoreBlazor.

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

Время повторного подключения по умолчанию использует вычисленную стратегию обратного выхода. Первые несколько попыток повторного подключения происходят в быстром последовательности, прежде чем вычисляемые задержки появляются между попытками. Логика по умолчанию для вычисления интервала повторных попыток — это сведения о реализации, подлежащие изменению без уведомления, но вы можете найти логику по умолчанию, которую Blazor платформа использует в computeDefaultRetryInterval функции (источник ссылок).

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Настройте поведение интервала повторных попыток, указав функцию для вычисления интервала повторных попыток. В следующем экспоненциальном примере обратной передачи число предыдущих попыток повторного подключения умножается на 1000 мс для вычисления интервала повтора. Если число предыдущих попыток повторного подключения () больше максимального предела повторных попыток (previousAttemptsmaxRetries), назначается интервал повторных попыток (retryIntervalMilliseconds), null чтобы прекратить дальнейшие попытки повторного подключения:

Blazor.start({
  circuit: {
    reconnectionOptions: {
      retryIntervalMilliseconds: (previousAttempts, maxRetries) => 
        previousAttempts >= maxRetries ? null : previousAttempts * 1000
    },
  },
});

Альтернативой является указание точной последовательности интервалов повторных попыток. После последнего указанного интервала повторных попыток повторите попытку, так как retryIntervalMilliseconds функция возвращает:undefined

Blazor.start({
  circuit: {
    reconnectionOptions: {
      retryIntervalMilliseconds: 
        Array.prototype.at.bind([0, 1000, 2000, 5000, 10000, 15000, 30000]),
    },
  },
});

Дополнительные сведения о запуске Blazor см. в статье Запуск ASP.NET Core Blazor.

Управление при появлении пользовательского интерфейса повторного подключения

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

  • Развернутое приложение часто отображает пользовательский интерфейс повторного подключения из-за времени ожидания связи, вызванных внутренней сетью или задержкой в Интернете, и вы хотите увеличить задержку.
  • Приложение должно сообщить пользователям о том, что подключение было удалено раньше, и вы хотите сократить задержку.

Время появления пользовательского интерфейса повторного подключения зависит от настройки интервала и времени ожидания времени ожидания на клиенте. Пользовательский интерфейс повторного подключения отображается при достижении времени ожидания сервера на клиенте (withServerTimeoutраздел конфигурации клиента). Однако изменение значения withServerTimeout требует изменений в других параметрах времени ожидания, времени ожидания и подтверждения, описанных в следующем руководстве.

В качестве общих рекомендаций для рекомендаций, приведенных ниже.

  • Интервал поддержания активности должен соответствовать конфигурациям клиента и сервера.
  • Время ожидания должно быть по крайней мере двойным значением, назначенным интервалу Keep-Alive.

Конфигурация сервера

Задайте следующие параметры:

  • ClientTimeoutInterval (по умолчанию: 30 секунд): клиенты временного периода должны отправлять сообщение, прежде чем сервер закрывает подключение.
  • HandshakeTimeout (по умолчанию: 15 секунд): интервал, используемый сервером для ожидания входящих запросов подтверждения клиентами.
  • KeepAliveInterval (по умолчанию: 15 секунд): интервал, используемый сервером для отправки связи в подключенных клиентах. Обратите внимание, что на клиенте также имеется параметр интервала "Сохранить жизнь", который должен соответствовать значению сервера.

HandshakeTimeout И ClientTimeoutInterval может быть увеличено, и KeepAliveInterval может остаться прежним. Важно учитывать, что при изменении значений убедитесь, что время ожидания по крайней мере удвоит значение интервала "Сохранить в живых" и совпадает с интервалом времени ожидания между сервером и клиентом. Дополнительные сведения см. в разделе "Настройка SignalR времени ожидания" и "Сохранение активности" в клиентском разделе.

В следующем примере :

  • Увеличивается ClientTimeoutInterval до 60 секунд (значение по умолчанию: 30 секунд).
  • Увеличивается HandshakeTimeout до 30 секунд (значение по умолчанию: 15 секунд).
  • Не KeepAliveInterval задан в коде разработчика и использует значение по умолчанию 15 секунд. Уменьшение значения интервала keep-Alive увеличивает частоту связи, что увеличивает нагрузку на приложение, сервер и сеть. Необходимо принять меры, чтобы избежать снижения производительности при снижении интервала "Сохранить жизнь".

Blazor Web App (.NET 8 или более поздней версии) в файле проекта Program сервера:

builder.Services.AddRazorComponents().AddInteractiveServerComponents()
    .AddHubOptions(options =>
{
    options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
    options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});

Blazor ServerProgram в файле:

builder.Services.AddServerSideBlazor()
    .AddHubOptions(options =>
    {
        options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
        options.HandshakeTimeout = TimeSpan.FromSeconds(30);
    });

Дополнительные сведения см. в разделе параметров обработчика канала на стороне сервера.

Настройка клиента

Задайте следующие параметры:

  • withServerTimeout (по умолчанию: 30 секунд): настраивает время ожидания сервера, указанное в миллисекундах, для подключения концентратора канала.
  • withKeepAliveInterval (по умолчанию: 15 секунд): интервал, указанный в миллисекундах, по которому соединение отправляет сообщения о сохранении активности.

Время ожидания сервера может быть увеличено, а интервал "Сохранить в живых" может оставаться неизменным. Важно учитывать, что при изменении значений убедитесь, что время ожидания сервера равно по крайней мере двойное значение интервала "Сохранить в живых", а значения интервала "Сохранить в живых" совпадают между сервером и клиентом. Дополнительные сведения см. в разделе "Настройка SignalR времени ожидания" и "Сохранение активности" в клиентском разделе.

В следующем примере конфигурации запуска (расположение скриптаBlazor) для времени ожидания сервера используется настраиваемое значение 60 секунд. Интервал keep-Alive (withKeepAliveInterval) не задан и использует значение по умолчанию в 15 секунд.

Blazor Web App:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    circuit: {
      configureSignalR: function (builder) {
        builder.withServerTimeout(60000);
      }
    }
  });
</script>

Blazor Server:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      builder.withServerTimeout(60000);
    }
  });
</script>

При создании подключения концентратора в компоненте задайте время ожидания сервера (WithServerTimeoutпо умолчанию: 30 секунд) на сервере HubConnectionBuilder. HandshakeTimeout Установите (по умолчанию: 15 секунд) на встроенном HubConnectionобъекте. Убедитесь, что тайм-ауты по крайней мере двойны интервала "Сохранить в живых" (WithKeepAliveInterval/KeepAliveInterval) и совпадают ли значения времени ожидания между сервером и клиентом.

Следующий пример основан на Index компоненте в руководствеSignalRBlazor. Время ожидания сервера увеличивается до 60 секунд, а время ожидания подтверждения увеличивается до 30 секунд. Интервал Keep-Alive не задан и использует значение по умолчанию в 15 секунд.

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .WithServerTimeout(TimeSpan.FromSeconds(60))
        .Build();

    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

Задайте следующие параметры:

  • serverTimeoutInMilliseconds (по умолчанию: 30 секунд): настраивает время ожидания сервера, указанное в миллисекундах, для подключения концентратора канала.
  • keepAliveIntervalInMilliseconds (по умолчанию: 15 секунд): интервал, указанный в миллисекундах, по которому соединение отправляет сообщения о сохранении активности.

Время ожидания сервера может быть увеличено, а интервал "Сохранить в живых" может оставаться неизменным. Важно учитывать, что при изменении значений убедитесь, что время ожидания сервера равно по крайней мере двойное значение интервала "Сохранить в живых", а значения интервала "Сохранить в живых" совпадают между сервером и клиентом. Дополнительные сведения см. в разделе "Настройка SignalR времени ожидания" и "Сохранение активности" в клиентском разделе.

В следующем примере конфигурации запуска (расположение скриптаBlazor) для времени ожидания сервера используется настраиваемое значение 60 секунд. Интервал keep-Alive (keepAliveIntervalInMilliseconds) не задан и использует значение по умолчанию в 15 секунд.

В Pages/_Host.cshtml:

<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
  Blazor.start({
    configureSignalR: function (builder) {
      let c = builder.build();
      c.serverTimeoutInMilliseconds = 60000;
      builder.build = () => {
        return c;
      };
    }
  });
</script>

При создании подключения концентратора в компоненте установите ServerTimeout (по умолчанию: 30 секунд) и HandshakeTimeout (по умолчанию: 15 секунд) на встроенном HubConnectionобъекте. Убедитесь, что время ожидания по крайней мере удвоит интервал времени ожидания. Убедитесь, что интервал "Сохранить в живых" совпадает между сервером и клиентом.

Следующий пример основан на Index компоненте в руководствеSignalRBlazor. Увеличивается ServerTimeout до 60 секунд, и HandshakeTimeout увеличивается до 30 секунд. Интервал keep-Alive (KeepAliveInterval) не задан и использует значение по умолчанию в 15 секунд.

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
        .Build();

    hubConnection.ServerTimeout = TimeSpan.FromSeconds(60);
    hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);

    hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...

    await hubConnection.StartAsync();
}

Отключение канала Blazor от клиента

Blazor Канал отключается при unload активации события страницы. Чтобы отключить канал от клиента в других сценариях, вызовите Blazor.disconnect в соответствующем обработчике событий. В следующем примере канал отключается, когда скрывается страница (событие pagehide):

window.addEventListener('pagehide', () => {
  Blazor.disconnect();
});

Дополнительные сведения о запуске Blazor см. в статье Запуск ASP.NET Core Blazor.

Обработчик канала на стороне сервера

Можно определить обработчик канала, который позволяет выполнять код при изменении состояния канала пользователя. Обработчик канала реализуется путем наследования от CircuitHandler и регистрации класса в контейнере службы приложения. В следующем примере обработчик канала отслеживает открытые соединения SignalR.

TrackingCircuitHandler.cs:

using Microsoft.AspNetCore.Components.Server.Circuits;

public class TrackingCircuitHandler : CircuitHandler
{
    private HashSet<Circuit> circuits = new();

    public override Task OnConnectionUpAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        circuits.Add(circuit);

        return Task.CompletedTask;
    }

    public override Task OnConnectionDownAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        circuits.Remove(circuit);

        return Task.CompletedTask;
    }

    public int ConnectedCircuits => circuits.Count;
}

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

В файле Program:

builder.Services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

В методе Startup.ConfigureServices в файле Startup.cs:

services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

Если методы пользовательского обработчика канала создают необработанное исключение, это исключение является неустранимым для канала . Чтобы допускать исключения в коде обработчика или вызываемых методах, заключите код в один или несколько операторов try-catch с обработкой ошибок и ведением журнала.

Когда канал завершается из-за того, что пользователь отключился и платформа очищает состояние канала, платформа удаляет область DI канала. При удалении области удаляются все службы DI, ограниченные каналом и реализующие System.IDisposable. Если любая служба DI вызывает необработанное исключение во время удаления, платформа регистрирует исключение. Дополнительные сведения см. в статье Внедрение зависимостей Blazor ASP.NET Core.

Обработчик канала на стороне сервера для записи пользователей для пользовательских служб

Используйте для CircuitHandler записи пользователя из AuthenticationStateProvider службы и задания этого пользователя в службе. Дополнительные сведения и пример кода см. в дополнительных сценариях безопасности на стороне сервера ASP.NET CoreBlazor.

Закрытие каналов при отсутствии оставшихся компонентов интерактивного сервера

Компоненты интерактивного сервера обрабатывают события веб-интерфейса с помощью подключения в режиме реального времени к браузеру, называемому каналом. Канал и связанное с ним состояние создаются при отрисовки корневого компонента интерактивного сервера. Канал закрывается, если на странице нет оставшихся компонентов интерактивного сервера, что освобождает ресурсы сервера.

IHttpContextAccessor/HttpContext в Razor компонентах

IHttpContextAccessor необходимо избежать интерактивной отрисовки, так как не существует допустимой HttpContext возможности.

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

HttpContext можно использовать в качестве каскадного параметра только в статически отрисованных корневых компонентах для общих задач, таких как проверка и изменение заголовков или других свойств компонента App (Components/App.razor). Значение всегда null предназначено для интерактивной отрисовки.

[CascadingParameter]
public HttpContext? HttpContext { get; set; }

В сценариях, где HttpContext требуется в интерактивных компонентах, рекомендуется передавать данные через постоянное состояние компонента с сервера. Дополнительные сведения см . на стороне сервера ASP.NET Основных Blazor дополнительных сценариев безопасности.

Не используйте IHttpContextAccessor/HttpContext непосредственно или косвенно в Razor компонентах серверных Blazor приложений. Blazor приложения выполняются вне контекста конвейера ASP.NET Core. Он HttpContext не гарантируется, что он доступен в пределах приложения IHttpContextAccessorи HttpContext не гарантируется, что он будет содержать контекст, который запустил Blazor приложение.

Рекомендуемый подход для передачи состояния Blazor запроса приложению осуществляется через параметры корневого компонента во время первоначальной отрисовки приложения. Кроме того, приложение может скопировать данные в службу с областью действия в событии жизненного цикла инициализации корневого компонента для использования в приложении. Дополнительные сведения см . на стороне сервера ASP.NET Основных Blazor дополнительных сценариев безопасности.

Критически важный аспект безопасности на стороне Blazor сервера заключается в том, что пользователь, подключенный к заданному каналу, может быть обновлен в какой-то момент после Blazor установки канала, но IHttpContextAccessorне обновляется. Дополнительные сведения об устранении этой ситуации с пользовательскими службами см. в дополнительных сценариях безопасности на стороне сервера ASP.NET CoreBlazor.

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