Udostępnij za pośrednictwem


wskazówki dotyczące platformy ASP.NET Core BlazorSignalR

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

W tym artykule wyjaśniono, jak konfigurować połączenia w Blazor aplikacjach i zarządzać nimiSignalR.

Aby uzyskać ogólne wskazówki dotyczące konfiguracji ASP.NET CoreSignalR, zobacz tematy w temacie Omówienie ASP.NET Core SignalR w dokumentacji, szczególnie w konfiguracji ASP.NET CoreSignalR.

Aplikacje po stronie serwera używają ASP.NET Core SignalR do komunikowania się z przeglądarką. SignalRWarunki hostingu i skalowania mają zastosowanie do aplikacji po stronie serwera.

Blazor działa najlepiej w przypadku korzystania z obiektów WebSocket jako SignalR transportu ze względu na mniejsze opóźnienia, niezawodność i bezpieczeństwo. Długie sondowanie jest używane, SignalR gdy zestawy WebSocket nie są dostępne lub gdy aplikacja jest jawnie skonfigurowana do korzystania z długiego sondowania.

Usługa platformy Azure SignalR z stanowym ponownym połączeniem

Stanowe ponowne połączenie (WithStatefulReconnect) zostało wydane za pomocą platformy .NET 8, ale nie jest obecnie obsługiwane dla usługi platformy Azure SignalR . Aby uzyskać więcej informacji, zobacz Stateful Reconnect Support? (Obsługa ponownego łączenia stanowego? (Azure/azure-signalr #1878).

Kompresja protokołu WebSocket dla składników interactive server

Domyślnie składniki interaktywnego serwera:

  • Włącz kompresję dla połączeń protokołu WebSocket. DisableWebSocketCompression (ustawienie domyślne: false) steruje kompresją protokołu WebSocket.

  • frame-ancestors Przyjęcie dyrektywy zasad zabezpieczeń zawartości (CSP) ustawionej na 'self'wartość , która zezwala tylko na osadzanie aplikacji w miejscu pochodzenia, z którego aplikacja jest obsługiwana, <iframe> gdy kompresja jest włączona lub gdy podano konfigurację kontekstu protokołu WebSocket. ContentSecurityFrameAncestorPolicy steruje dostawcą frame-ancestors CSP.

Dostawca frame-ancestors CSP można usunąć ręcznie, ustawiając wartość ContentSecurityFrameAncestorsPolicy na null, ponieważ można skonfigurować dostawcę CSP w scentralizowany sposób. frame-ancestors Gdy dostawca usług kryptograficznych jest zarządzany w sposób scentralizowany, należy zadbać o zastosowanie zasad za każdym razem, gdy pierwszy dokument jest renderowany. Nie zalecamy całkowitego usuwania zasad, ponieważ może to spowodować, że aplikacja będzie podatna na ataki.

Służy ConfigureWebSocketAcceptContext do konfigurowania WebSocketAcceptContext połączeń protokołu websocket używanych przez składniki serwera. Domyślnie są stosowane zasady, które umożliwiają kompresję i ustawiają dostawcę CSP dla elementów podrzędnych ramek zdefiniowanych w ContentSecurityFrameAncestorsPolicy programie.

Przykłady użycia:

Wyłącz kompresję, ustawiając wartość DisableWebSocketCompression true, co zmniejsza lukę w zabezpieczeniach aplikacji do ataku , ale może spowodować zmniejszenie wydajności:

builder.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(o => o.DisableWebSocketCompression = true)

Po włączeniu kompresji należy skonfigurować bardziej frame-ancestors rygorystyczny dostawca usług kryptograficznych z wartością 'none' (wymagane pojedyncze cudzysłowy), co umożliwia kompresję protokołu WebSocket, ale uniemożliwia przeglądarkom osadzanie aplikacji w dowolnym elemencie <iframe>:

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

Po włączeniu kompresji usuń dostawcę frame-ancestors CSP, ustawiając wartość nullContentSecurityFrameAncestorsPolicy . Ten scenariusz jest zalecany tylko w przypadku aplikacji, które ustawiają dostawcę CSP w scentralizowany sposób:

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

Ważne

Przeglądarki stosują dyrektywy CSP z wielu nagłówków CSP przy użyciu najściślejszej wartości dyrektywy zasad. W związku z tym deweloper nie może dodać słabszych frame-ancestors zasad niż 'self' celowo ani przez pomyłkę.

Pojedyncze cudzysłowy są wymagane dla wartości ciągu przekazanej do ContentSecurityFrameAncestorsPolicyelementu :

Nieobsługiwane wartości: none, self

'self'

Dodatkowe opcje obejmują określanie co najmniej jednego źródła hosta i źródeł schematu.

Aby uzyskać informacje na temat wpływu na zabezpieczenia, zobacz Wskazówki dotyczące ograniczania zagrożeń dotyczące renderowania interaktywnego po stronie serwera ASP.NET CoreBlazor. Aby uzyskać więcej informacji na frame-ancestors temat dyrektywy, zobacz CSP: frame-ancestors (dokumentacja MDN).

Wyłączanie kompresji odpowiedzi dla Przeładowywanie na gorąco

W przypadku korzystania z Przeładowywanie na gorąco wyłącz oprogramowanie pośredniczące kompresji odpowiedzi w Development środowisku. Niezależnie od tego, czy jest używany domyślny kod z szablonu projektu, zawsze należy wywołać UseResponseCompression najpierw w potoku przetwarzania żądań.

W pliku Program:

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

Negocjacje między źródłami po stronie SignalR klienta na potrzeby uwierzytelniania

W tej sekcji wyjaśniono, jak skonfigurować SignalRbazowego klienta do wysyłania poświadczeń, takich jak pliki cookie lub nagłówki uwierzytelniania HTTP.

Służy SetBrowserRequestCredentials do ustawiania Include żądań między źródłami 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);
    }
}

W przypadku skompilowania połączenia koncentratora przypisz HttpMessageHandler tę opcję:HttpMessageHandlerFactory

private HubConnectionBuilder? hubConnection;

...

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

W poprzednim przykładzie adres URL połączenia koncentratora jest konfigurowany na bezwzględny adres URI pod adresem /chathub. Identyfikator URI można również ustawić za pomocą ciągu, na przykład https://signalr.example.com, lub za pośrednictwem konfiguracji. Navigation jest wstrzykniętym NavigationManagerelementem .

Aby uzyskać więcej informacji, zobacz ASP.NET Core configuration (Konfiguracja ASP.NET CoreSignalR).

Renderowanie po stronie klienta

W przypadku skonfigurowania prerenderingu przed nawiązaniem połączenia klienta z serwerem następuje wstępneenderowanie. Aby uzyskać więcej informacji, zobacz Prerender ASP.NET Core components (Składniki prerender ASP.NET CoreRazor).

W przypadku skonfigurowania prerenderingu przed nawiązaniem połączenia klienta z serwerem następuje wstępneenderowanie. Aby uzyskać więcej informacji, zobacz następujące artykuły:

Rozmiar stanu wstępnego i SignalR limit rozmiaru komunikatu

Duży rozmiar stanu wstępnego może przekroczyć Blazorlimit rozmiaru komunikatu obwodu SignalR , co powoduje następujące kwestie:

  • Nie SignalR można zainicjować obwodu z powodu błędu na kliencie: Circuit host not initialized.
  • Interfejs użytkownika ponownego nawiązywania połączenia na kliencie jest wyświetlany, gdy obwód ulegnie awarii. Odzyskiwanie nie jest możliwe.

Aby rozwiązać ten problem, użyj jednej z następujących metod:

  • Zmniejsz ilość danych umieszczanych w stanie wstępnie utworzonym.
  • Zwiększ limit rozmiaru komunikatu.SignalR OSTRZEŻENIE: Zwiększenie limitu może zwiększyć ryzyko ataków typu "odmowa usługi" (DoS).

Dodatkowe zasoby po stronie klienta

Używanie koligacji sesji (sesji lepkich) na potrzeby hostingu farmy internetowej po stronie serwera

Gdy jest używany więcej niż jeden serwer zaplecza, aplikacja musi zaimplementować koligację sesji, nazywaną również sesjami sticky. Koligacja sesji gwarantuje, że obwód klienta ponownie łączy się z tym samym serwerem, jeśli połączenie zostanie przerwane, co jest ważne, ponieważ stan klienta jest przechowywany tylko w pamięci serwera, który po raz pierwszy ustanowił obwód klienta.

Następujący błąd jest zgłaszany przez aplikację, która nie włączyła koligacji sesji w farmie internetowej:

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

Aby uzyskać więcej informacji na temat koligacji sesji z hostingiem usługi aplikacja systemu Azure, zobacz Hostowanie i wdrażanie aplikacji po stronie Blazor serwera ASP.NET Core.

Usługa platformy Azure SignalR

Opcjonalna usługa platformy Azure SignalR działa w połączeniu z centrum aplikacji SignalR w celu skalowania aplikacji w górę aplikacji po stronie serwera do dużej liczby współbieżnych połączeń. Ponadto globalne zasięg usługi i centra danych o wysokiej wydajności znacznie pomagają zmniejszyć opóźnienia ze względu na lokalizację geograficzną.

Usługa nie jest wymagana w przypadku Blazor aplikacji hostowanych w usłudze aplikacja systemu Azure Lub Azure Container Apps, ale może być przydatna w innych środowiskach hostingu:

  • Aby ułatwić skalowanie połączeń w poziomie.
  • Obsługa dystrybucji globalnej.

Aby uzyskać więcej informacji, zobacz Hostowanie i wdrażanie aplikacji po stronie Blazor serwera ASP.NET Core.

Opcje obsługi obwodu po stronie serwera

Skonfiguruj obwód za pomocą polecenia CircuitOptions. Wyświetl wartości domyślne w źródle odwołania.

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Odczytywanie lub ustawianie opcji w Program pliku za pomocą delegata opcji na AddInteractiveServerComponents. Symbol {OPTION} zastępczy reprezentuje opcję, a {VALUE} symbol zastępczy jest wartością.

W pliku Program:

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

Odczytywanie lub ustawianie opcji w Program pliku za pomocą delegata opcji na AddServerSideBlazor. Symbol {OPTION} zastępczy reprezentuje opcję, a {VALUE} symbol zastępczy jest wartością.

W pliku Program:

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

Przeczytaj lub ustaw opcje w programie Startup.ConfigureServices za pomocą delegata opcji na AddServerSideBlazor. Symbol {OPTION} zastępczy reprezentuje opcję, a {VALUE} symbol zastępczy jest wartością.

W Startup.ConfigureServices pliku :Startup.cs

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

Aby skonfigurować element HubConnectionContext, użyj polecenia z AddHubOptions.HubConnectionContextOptions Wyświetl wartości domyślne opcji kontekstu połączenia centrum w źródle referencyjnym. Opisy opcji w SignalR dokumentacji można znaleźć w artykule ASP.NET Core configuration (Konfiguracja ASP.NET CoreSignalR). Symbol {OPTION} zastępczy reprezentuje opcję, a {VALUE} symbol zastępczy jest wartością.

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

W pliku Program:

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

W pliku Program:

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

W Startup.ConfigureServices pliku :Startup.cs

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

Ostrzeżenie

Wartość domyślna to MaximumReceiveMessageSize 32 KB. Zwiększenie wartości może zwiększyć ryzyko ataków typu "odmowa usługi" (DoS).

Blazor wartość jest ustawiona na MaximumParallelInvocationsPerClient wartość 1, która jest wartością domyślną. Aby uzyskać więcej informacji, zobacz MaximumParallelInvocationsPerClient > 1 przerywa przekazywanie plików w Blazor Server trybie (dotnet/aspnetcore #53951).

Aby uzyskać informacje na temat zarządzania pamięcią, zobacz Hostowanie i wdrażanie aplikacji po stronie Blazor serwera ASP.NET Core.

Blazor opcje koncentratora

Skonfiguruj MapBlazorHub opcje sterowania HttpConnectionDispatcherOptions koncentratorem Blazor . Wyświetl wartości domyślne opcji dyspozytora połączeń koncentratora w źródle referencyjnym. Symbol {OPTION} zastępczy reprezentuje opcję, a {VALUE} symbol zastępczy jest wartością.

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Umieść wywołanie metody po app.MapBlazorHub wywołaniu metody app.MapRazorComponents w pliku aplikacji Program :

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

Konfigurowanie koncentratora używanego przez AddInteractiveServerRenderMode program z niepowodzeniem z MapBlazorHub powodu błędu AmbiguousMatchException:

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

Aby obejść problem dotyczący aplikacji przeznaczonych dla platformy .NET 8, nadaj niestandardowemu Blazor centrum wyższe pierwszeństwo przy użyciu WithOrder metody :

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

Aby uzyskać więcej informacji, zobacz następujące zasoby:

Podaj opcje app.MapBlazorHub w pliku aplikacji Program :

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

Podaj opcje w app.MapBlazorHub konfiguracji routingu punktu końcowego:

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

Maksymalny rozmiar komunikatu odbioru

Ta sekcja dotyczy tylko projektów implementujących SignalRprogram .

Maksymalny rozmiar komunikatu przychodzącego SignalR dozwolony dla metod centrum jest ograniczony ( HubOptions.MaximumReceiveMessageSize domyślnie: 32 KB). SignalR komunikaty większe niż MaximumReceiveMessageSize zgłaszają błąd. Struktura nie nakłada limitu rozmiaru komunikatu SignalR z centrum na klienta.

Jeśli SignalR rejestrowanie nie jest ustawione na debugowanie lub śledzenie, w konsoli narzędzi deweloperskich przeglądarki pojawia się tylko błąd rozmiaru komunikatu:

Błąd: Połączenie zostało rozłączone z powodu błędu "Błąd: Serwer zwrócił błąd podczas zamykania: połączenie zostało zamknięte z powodu błędu".

Gdy SignalR rejestrowanie po stronie serwera jest ustawione na debugowanie lub śledzenie, rejestrowanie po stronie serwera wyświetla InvalidDataException błąd rozmiaru komunikatu.

appsettings.Development.json:

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

Błąd:

System.IO.InvalidDataException: przekroczono maksymalny rozmiar komunikatu 32768B. Rozmiar komunikatu można skonfigurować w obszarze AddHubOptions.

Jedno podejście polega na zwiększeniu limitu przez ustawienie MaximumReceiveMessageSize w Program pliku. Poniższy przykład ustawia maksymalny rozmiar komunikatu odbioru na 64 KB:

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

Zwiększenie limitu rozmiaru SignalR komunikatów przychodzących wiąże się z kosztem wymagania większej ilości zasobów serwera i zwiększa ryzyko ataków typu "odmowa usługi" (DoS). Ponadto odczytywanie dużej ilości zawartości w pamięci jako ciągów lub tablic bajtów może również spowodować alokacje, które działają źle z modułem odśmiecenia pamięci, co powoduje dodatkowe kary za wydajność.

Lepszym rozwiązaniem do odczytywania dużych ładunków jest wysłanie zawartości w mniejszych fragmentach i przetworzenie ładunku jako Stream. Może to być używane podczas odczytywania dużych ładunków JSON międzyoperacyjności języka JavaScript (JS) lub jeśli JS dane międzyoperacyjne są dostępne jako nieprzetworzone bajty. Przykład przedstawiający wysyłanie dużych ładunków binarnych w aplikacjach po stronie serwera, które używają technik podobnych do składnika, zobacz przykładową aplikację Binary Submit iInputLargeTextAreaBlazor Przykładowy składnik.InputFile

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Formularze, które przetwarzają duże ładunki, mogą również bezpośrednio używać międzyoperacyjności SignalR przesyłania strumieniowego JS . Aby uzyskać więcej informacji, zobacz Wywoływanie metod platformy .NET z funkcji Języka JavaScript w programie ASP.NET Core Blazor. Aby zapoznać się z przykładem formularzy przesyłających strumieniowo dane do serwera, zobacz Rozwiązywanie problemów z formularzami <textarea>ASP.NET CoreBlazor.

Jedno podejście polega na zwiększeniu limitu przez ustawienie MaximumReceiveMessageSize w Program pliku. Poniższy przykład ustawia maksymalny rozmiar komunikatu odbioru na 64 KB:

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

Zwiększenie limitu rozmiaru SignalR komunikatów przychodzących wiąże się z kosztem wymagania większej ilości zasobów serwera i zwiększa ryzyko ataków typu "odmowa usługi" (DoS). Ponadto odczytywanie dużej ilości zawartości w pamięci jako ciągów lub tablic bajtów może również spowodować alokacje, które działają źle z modułem odśmiecenia pamięci, co powoduje dodatkowe kary za wydajność.

Lepszym rozwiązaniem do odczytywania dużych ładunków jest wysłanie zawartości w mniejszych fragmentach i przetworzenie ładunku jako Stream. Może to być używane podczas odczytywania dużych ładunków JSON międzyoperacyjności języka JavaScript (JS) lub jeśli JS dane międzyoperacyjne są dostępne jako nieprzetworzone bajty. Aby zapoznać się z przykładem wysyłania dużych ładunków binarnych w Blazor Server programie, które używają technik podobnych do InputFile składnika, zobacz przykładową aplikację Binary Submit i BlazorInputLargeTextArea przykładowy składnik.

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Formularze, które przetwarzają duże ładunki, mogą również bezpośrednio używać międzyoperacyjności SignalR przesyłania strumieniowego JS . Aby uzyskać więcej informacji, zobacz Wywoływanie metod platformy .NET z funkcji Języka JavaScript w programie ASP.NET Core Blazor. Aby zapoznać się z przykładem formularzy przesyłających strumieniowo dane w Blazor Server aplikacji, zobacz Rozwiązywanie problemów z formularzami <textarea>ASP.NET CoreBlazor.

Zwiększ limit, ustawiając wartość w pliku MaximumReceiveMessageSize Startup.ConfigureServices:

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

Zwiększenie limitu rozmiaru SignalR komunikatów przychodzących wiąże się z kosztem wymagania większej ilości zasobów serwera i zwiększa ryzyko ataków typu "odmowa usługi" (DoS). Ponadto odczytywanie dużej ilości zawartości w pamięci jako ciągów lub tablic bajtów może również spowodować alokacje, które działają źle z modułem odśmiecenia pamięci, co powoduje dodatkowe kary za wydajność.

Podczas tworzenia kodu, który przesyła dużą ilość danych, należy wziąć pod uwagę następujące wskazówki:

  • Podziel dane na mniejsze elementy i wysyłaj segmenty danych sekwencyjnie do momentu odebrania wszystkich danych przez serwer.
  • Nie przydzielaj dużych obiektów w JS kodzie i C#.
  • Nie blokuj głównego wątku interfejsu użytkownika przez długi czas podczas wysyłania lub odbierania danych.
  • Bezpłatna ilość zużytej pamięci po zakończeniu lub anulowaniu procesu.
  • Wymuś następujące dodatkowe wymagania dotyczące zabezpieczeń:
    • Zadeklaruj maksymalny rozmiar pliku lub danych, który można przekazać.
    • Zadeklaruj minimalną szybkość przekazywania od klienta do serwera.
  • Po odebraniu danych przez serwer dane mogą być następujące:
    • Tymczasowo przechowywane w buforze pamięci do momentu zebrania wszystkich segmentów.
    • Zużytych natychmiast. Na przykład dane mogą być przechowywane natychmiast w bazie danych lub zapisywane na dysku w miarę odbierania poszczególnych segmentów.

Blazor Konfiguracja trasy punktu końcowego koncentratora po stronie serwera

W pliku wywołaj Program metodę MapBlazorHub , aby zamapować BlazorHub element na domyślną ścieżkę aplikacji. Skrypt Blazor (blazor.*.js) automatycznie wskazuje punkt końcowy utworzony przez MapBlazorHubprogram .

Odzwierciedlanie stanu połączenia po stronie serwera w interfejsie użytkownika

Jeśli klient wykryje utracone połączenie z serwerem, domyślny interfejs użytkownika jest wyświetlany użytkownikowi, gdy klient próbuje ponownie nawiązać połączenie:

Domyślny interfejs użytkownika ponownego łączenia.

Domyślny interfejs użytkownika ponownego łączenia.

Jeśli ponowne nawiązanie połączenia nie powiedzie się, użytkownik zostanie poinstruowany, aby ponowić próbę lub ponownie załadować stronę:

Domyślny interfejs użytkownika ponawiania prób.

Domyślny interfejs użytkownika ponawiania prób.

Jeśli ponowne nawiązanie połączenia powiedzie się, stan użytkownika jest często utracony. Kod niestandardowy można dodać do dowolnego składnika w celu zapisania i ponownego załadowania stanu użytkownika w przypadku niepowodzeń połączenia. Aby uzyskać więcej informacji, zobacz zarządzanie stanem ASP.NET CoreBlazor.

Aby dostosować interfejs użytkownika, zdefiniuj pojedynczy element z elementem id components-reconnect-modal <body> w zawartości elementu. Poniższy przykład umieszcza element w składniku App .

App.razor:

Aby dostosować interfejs użytkownika, zdefiniuj pojedynczy element z elementem id components-reconnect-modal <body> w zawartości elementu. Poniższy przykład umieszcza element na stronie hosta.

Pages/_Host.cshtml:

Aby dostosować interfejs użytkownika, zdefiniuj pojedynczy element z elementem id components-reconnect-modal <body> w zawartości elementu. Poniższy przykład umieszcza element na stronie układu.

Pages/_Layout.cshtml:

Aby dostosować interfejs użytkownika, zdefiniuj pojedynczy element z elementem id components-reconnect-modal <body> w zawartości elementu. Poniższy przykład umieszcza element na stronie hosta.

Pages/_Host.cshtml:

<div id="components-reconnect-modal">
    Connection lost.<br>Attempting to reconnect...
</div>

Uwaga

Jeśli aplikacja renderuje więcej niż jeden element z elementem id components-reconnect-modal , tylko pierwszy renderowany element otrzymuje zmiany klasy CSS w celu wyświetlenia lub ukrycia elementu.

Dodaj następujące style CSS do arkusza stylów witryny.

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;
    background-color: white;
    padding: 2rem;
    border-radius: 0.5rem;
    text-align: center;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    margin: 50px 50px;
    position: fixed;
    top: 0;
    z-index: 10001;
}

W poniższej tabeli opisano klasy CSS zastosowane do components-reconnect-modal elementu przez platformę Blazor .

Klasa CSS Wskazuje...
components-reconnect-show Utracone połączenie. Klient próbuje ponownie nawiązać połączenie. Pokaż modalne.
components-reconnect-hide Aktywne połączenie zostanie ponownie nawiązane z serwerem. Ukryj modalne.
components-reconnect-failed Ponowne nawiązywanie połączenia nie powiodło się, prawdopodobnie z powodu awarii sieci. Aby spróbować ponownie nawiązać połączenie, wywołaj metodę window.Blazor.reconnect() w języku JavaScript.
components-reconnect-rejected Ponowne połączenie odrzucone. Serwer został osiągnięty, ale odmówił połączenia, a stan użytkownika na serwerze zostanie utracony. Aby ponownie załadować aplikację, wywołaj metodę location.reload() w języku JavaScript. Ten stan połączenia może spowodować, że:
  • Występuje awaria obwodu po stronie serwera.
  • Klient jest odłączony wystarczająco długo, aby serwer porzucił stan użytkownika. Wystąpienia składników użytkownika są usuwane.
  • Serwer jest uruchamiany ponownie lub proces roboczy aplikacji jest odzyskiwane.

Dostosuj opóźnienie przed wyświetleniem interfejsu użytkownika ponownego połączenia, ustawiając transition-delay właściwość w pliku CSS witryny dla elementu modalnego. W poniższym przykładzie ustawiono opóźnienie przejścia z 500 ms (wartość domyślna) na 1000 ms (1 sekunda).

wwwroot/app.css:

wwwroot/css/site.css:

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

Aby wyświetlić bieżącą próbę ponownego nawiązania połączenia, zdefiniuj element z wartością id components-reconnect-current-attempt. Aby wyświetlić maksymalną liczbę ponownych prób ponownego połączenia, zdefiniuj element z wartością id components-reconnect-max-retries. Poniższy przykład umieszcza te elementy wewnątrz elementu ponowić próbę połączenia modalne po poprzednim przykładzie.

<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>

Gdy pojawi się modalne ponowne połączenie niestandardowe, renderuje następującą zawartość z licznikiem ponownego połączenia:

Wystąpił problem z połączeniem! (Bieżąca próba ponownego połączenia: 1/8)

Renderowanie po stronie serwera

Domyślnie składniki są wstępnie obsługiwane na serwerze przed nawiązaniem połączenia klienta z serwerem. Aby uzyskać więcej informacji, zobacz Prerender ASP.NET Core components (Składniki prerender ASP.NET CoreRazor).

Domyślnie składniki są wstępnie obsługiwane na serwerze przed nawiązaniem połączenia klienta z serwerem. Aby uzyskać więcej informacji, zobacz Pomocnik tagów składników w programie ASP.NET Core.

Monitorowanie aktywności obwodu po stronie serwera

Monitoruj aktywność obwodu przychodzącego CreateInboundActivityHandler przy użyciu metody w pliku CircuitHandler. Działanie obwodu przychodzącego to wszelkie działania wysyłane z przeglądarki do serwera, takie jak zdarzenia interfejsu użytkownika lub JavaScript-to-.NET wywołania międzyoperacowe.

Można na przykład użyć programu obsługi działań obwodu, aby wykryć, czy klient jest w stanie bezczynności i zarejestrować jego identyfikator obwodu (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;
    }
}

Zarejestruj usługę Program w pliku. Poniższy przykład konfiguruje domyślny limit czasu bezczynności od pięciu minut do pięciu sekund w celu przetestowania poprzedniej IdleCircuitHandler implementacji:

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

Programy obsługi działań obwodu zapewniają również podejście do uzyskiwania dostępu do usług o określonym Blazor zakresie z innychBlazor zakresów iniekcji zależności (DI). Aby uzyskać więcej informacji i przykładów, zobacz:

Blazor startup

Skonfiguruj ręczne uruchamianie obwodu Blazorw SignalR App.razor pliku Blazor Web App:

Skonfiguruj ręczne uruchamianie obwodu BlazorSignalR w Pages/_Host.cshtml pliku (Blazor Server):

Skonfiguruj ręczne uruchamianie obwodu BlazorSignalR w Pages/_Layout.cshtml pliku (Blazor Server):

Skonfiguruj ręczne uruchamianie obwodu BlazorSignalR w Pages/_Host.cshtml pliku (Blazor Server):

  • autostart="false" Dodaj atrybut do tagu <script> skryptublazor.*.js.
  • Umieść skrypt wywołujący Blazor.start() po załadowaniu skryptu Blazor i wewnątrz tagu zamykającego </body> .

Gdy autostart funkcja jest wyłączona, każdy aspekt aplikacji, który nie zależy od obwodu, działa normalnie. Na przykład routing po stronie klienta działa. Jednak każdy aspekt, który zależy od obwodu, nie działa, dopóki Blazor.start() nie zostanie wywołany. Zachowanie aplikacji jest nieprzewidywalne bez ustalonego obwodu. Na przykład metody składników nie mogą być wykonywane, gdy obwód jest odłączony.

Aby uzyskać więcej informacji, w tym sposób inicjowania Blazor , gdy dokument jest gotowy i jak utworzyć łańcuch do elementu JS Promise, zobacz ASP.NET Core Blazor start.

Konfigurowanie SignalR limitów czasu i zachowania aktywności na kliencie

Skonfiguruj następujące wartości dla klienta:

  • withServerTimeout: konfiguruje limit czasu serwera w milisekundach. Jeśli ten limit czasu upłynie bez odbierania komunikatów z serwera, połączenie zostanie zakończone z powodu błędu. Domyślna wartość limitu czasu to 30 sekund. Limit czasu serwera powinien być co najmniej dwukrotnie większy niż wartość przypisana do interwału Keep-Alive (withKeepAliveInterval).
  • withKeepAliveInterval: konfiguruje interwał Keep-Alive w milisekundach (domyślny interwał, w którym ma być wysyłana polecenie ping do serwera). To ustawienie umożliwia serwerowi wykrywanie twardych rozłączeń, takich jak odłączanie komputera od sieci przez klienta. Polecenie ping występuje co najwyżej w przypadku ping serwera. Jeśli serwer wysyła polecenia ping co pięć sekund, przypisywanie wartości niższej niż 5000 (5 sekund) ping co pięć sekund. Wartość domyślna to 15 sekund. Interwał Keep-Alive powinien być mniejszy lub równy połowie wartości przypisanej do limitu czasu serwera (withServerTimeout).

Poniższy przykład dla App.razor pliku (Blazor Web App) pokazuje przypisanie wartości domyślnych.

Blazor Web App:

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

Poniższy przykład dla Pages/_Host.cshtml pliku (Blazor Serverwszystkie wersje z wyjątkiem ASP.NET Core na platformie .NET 6) lub Pages/_Layout.cshtml plik (Blazor ServerASP.NET Core na platformie .NET 6).

Blazor Server:

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

W poprzednim przykładzie {BLAZOR SCRIPT} symbol zastępczy to ścieżka skryptu Blazor i nazwa pliku. Aby uzyskać informacje o lokalizacji skryptu i ścieżce do użycia, zobacz ASP.NET Core project structure (Struktura projektu ASP.NET CoreBlazor).

Podczas tworzenia połączenia koncentratora w składniku ustaw ServerTimeout wartość (domyślną: 30 sekund) i KeepAliveInterval (domyślnie: 15 sekund) na .HubConnectionBuilder Ustaw wartość (domyślną HandshakeTimeout : 15 sekund) na skompilowanych HubConnectionelementach . W poniższym przykładzie pokazano przypisanie wartości domyślnych:

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();
}

Skonfiguruj następujące wartości dla klienta:

  • serverTimeoutInMilliseconds: limit czasu serwera w milisekundach. Jeśli ten limit czasu upłynie bez odbierania komunikatów z serwera, połączenie zostanie zakończone z powodu błędu. Domyślna wartość limitu czasu to 30 sekund. Limit czasu serwera powinien być co najmniej dwukrotnie większy niż wartość przypisana do interwału Keep-Alive (keepAliveIntervalInMilliseconds).
  • keepAliveIntervalInMilliseconds: domyślny interwał ping do serwera. To ustawienie umożliwia serwerowi wykrywanie twardych rozłączeń, takich jak odłączanie komputera od sieci przez klienta. Polecenie ping występuje co najwyżej w przypadku ping serwera. Jeśli serwer wysyła polecenia ping co pięć sekund, przypisywanie wartości niższej niż 5000 (5 sekund) ping co pięć sekund. Wartość domyślna to 15 sekund. Interwał Keep-Alive powinien być mniejszy lub równy połowie wartości przypisanej do limitu czasu serwera (serverTimeoutInMilliseconds).

Poniższy przykład dla Pages/_Host.cshtml pliku (Blazor Serverwszystkie wersje z wyjątkiem ASP.NET Core na platformie .NET 6) lub Pages/_Layout.cshtml plik (Blazor ServerASP.NET Core na platformie .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>

W poprzednim przykładzie {BLAZOR SCRIPT} symbol zastępczy to ścieżka skryptu Blazor i nazwa pliku. Aby uzyskać informacje o lokalizacji skryptu i ścieżce do użycia, zobacz ASP.NET Core project structure (Struktura projektu ASP.NET CoreBlazor).

Podczas tworzenia połączenia koncentratora w składniku ustaw ServerTimeout wartość (domyślna: 30 sekund), HandshakeTimeout (wartość domyślna: 15 sekund) i KeepAliveInterval (wartość domyślna: 15 sekund) na skompilowanych HubConnectionelementach . W poniższym przykładzie pokazano przypisanie wartości domyślnych:

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();
}

Podczas zmieniania wartości limitu czasu serwera (ServerTimeout) lub interwału Keep-Alive (KeepAliveInterval):

  • Limit czasu serwera powinien być co najmniej dwukrotnie większy niż wartość przypisana do interwału Keep-Alive.
  • Interwał Keep-Alive powinien być mniejszy lub równy połowie wartości przypisanej do limitu czasu serwera.

Aby uzyskać więcej informacji, zobacz sekcje Global deployment and connection failures (Globalne błędy wdrażania i połączeń) w następujących artykułach:

Modyfikowanie programu obsługi ponownego łączenia po stronie serwera

Zdarzenia połączenia obwodu programu obsługi ponownego połączenia można modyfikować pod kątem zachowań niestandardowych, takich jak:

  • Aby powiadomić użytkownika o usunięciu połączenia.
  • Aby wykonać rejestrowanie (z klienta), gdy obwód jest połączony.

Aby zmodyfikować zdarzenia połączenia, zarejestruj wywołania zwrotne dla następujących zmian połączenia:

  • Porzucone połączenia używają polecenia onConnectionDown.
  • Nawiązane/ponownie nawiązane połączenia używają polecenia onConnectionUp.

Należy określić oba onConnectionDown elementy i 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>

W poprzednim przykładzie {BLAZOR SCRIPT} symbol zastępczy to ścieżka skryptu Blazor i nazwa pliku. Aby uzyskać informacje o lokalizacji skryptu i ścieżce do użycia, zobacz ASP.NET Core project structure (Struktura projektu ASP.NET CoreBlazor).

Automatyczne odświeżanie strony w przypadku niepowodzenia ponownego nawiązania połączenia po stronie serwera

Domyślne zachowanie ponownego nawiązywania połączenia wymaga od użytkownika ręcznego wykonania akcji w celu odświeżenia strony po niepomyślnie nawiązaniu połączenia. Można jednak użyć niestandardowego programu obsługi ponownego łączenia, aby automatycznie odświeżyć stronę:

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>

W poprzednim przykładzie {BLAZOR SCRIPT} symbol zastępczy to ścieżka skryptu Blazor i nazwa pliku. Aby uzyskać informacje o lokalizacji skryptu i ścieżce do użycia, zobacz ASP.NET Core project structure (Struktura projektu ASP.NET CoreBlazor).

Utwórz następujący wwwroot/boot.js plik.

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;
      }
    }
  });
})();

Aby uzyskać więcej informacji na temat uruchamiania platformy Blazor, zobacz Uruchamianie platformy ASP.NET Core Blazor.

Dostosowywanie liczby ponownych prób i interwału ponownego połączenia po stronie serwera

Aby dostosować liczbę ponownych prób połączenia i interwał, ustaw liczbę ponownych prób (maxRetries) i okres w milisekundach dozwolonych dla każdej próby ponawiania próby (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>

W poprzednim przykładzie {BLAZOR SCRIPT} symbol zastępczy to ścieżka skryptu Blazor i nazwa pliku. Aby uzyskać informacje o lokalizacji skryptu i ścieżce do użycia, zobacz ASP.NET Core project structure (Struktura projektu ASP.NET CoreBlazor).

Gdy użytkownik przechodzi z powrotem do aplikacji z odłączonym obwodem, następuje natychmiastowe ponowne nawiązanie połączenia, a nie oczekiwanie na czas trwania następnego interwału ponownego połączenia. To zachowanie ma na celu wznowienie połączenia tak szybko, jak to możliwe dla użytkownika.

Domyślny czas ponownego nawiązywania połączenia używa obliczonej strategii wycofywania. Pierwsze kilka ponownych prób połączenia następuje w krótkim odstępie czasu, zanim zostaną wprowadzone obliczone opóźnienia między próbami. Domyślna logika przetwarzania interwału ponawiania prób to szczegóły implementacji, które mogą ulec zmianie bez powiadomienia, ale można znaleźć domyślną logikę Blazor używaną przez platformę w computeDefaultRetryInterval funkcji (źródło odwołania).

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Dostosuj zachowanie interwału ponawiania, określając funkcję, aby obliczyć interwał ponawiania prób. W poniższym przykładzie wycofywania wykładniczego liczba poprzednich prób ponownego połączenia jest pomnożona przez 1000 ms w celu obliczenia interwału ponawiania prób. Jeśli liczba poprzednich prób ponownego nawiązania połączenia (previousAttempts) jest większa niż maksymalny limit ponawiania prób (maxRetries), null jest przypisywana do interwału ponawiania prób (retryIntervalMilliseconds) w celu zaprzestania dalszych prób ponownego nawiązania połączenia:

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

Alternatywą jest określenie dokładnej sekwencji interwałów ponawiania prób. Po ostatnim określonym interwale ponawiania próby ponowimy próbę zatrzymania, ponieważ retryIntervalMilliseconds funkcja zwraca wartość undefined:

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

Aby uzyskać więcej informacji na temat uruchamiania platformy Blazor, zobacz Uruchamianie platformy ASP.NET Core Blazor.

Kontrolka po wyświetleniu interfejsu użytkownika ponownego połączenia

Kontrolowanie, kiedy interfejs użytkownika ponownego łączenia może być przydatny w następujących sytuacjach:

  • Wdrożona aplikacja często wyświetla interfejs użytkownika ponownego nawiązywania połączenia z powodu przekroczenia limitu czasu ping spowodowanych przez sieć wewnętrzną lub opóźnienie internetowe i chcesz zwiększyć opóźnienie.
  • Aplikacja powinna zgłaszać użytkownikom, że połączenie spadło wcześniej i chcesz skrócić opóźnienie.

Czas wyświetlania interfejsu użytkownika ponownego nawiązywania połączenia ma wpływ na dostosowanie interwału Keep-Alive i limitów czasu na kliencie. Interfejs użytkownika ponownego nawiązywania połączenia jest wyświetlany po osiągnięciu limitu czasu serwera na kliencie (withServerTimeoutsekcja Konfiguracja klienta). Jednak zmiana wartości parametru withServerTimeout wymaga zmian w innych ustawieniach Keep-Alive, limitu czasu i uzgadniania opisanych w poniższych wskazówkach.

Zgodnie z ogólnymi zaleceniami dotyczącymi poniższych wskazówek:

  • Interwał Keep-Alive powinien być zgodny z konfiguracjami klienta i serwera.
  • Limity czasu powinny mieć co najmniej dwukrotnie wartość przypisaną do interwału Keep-Alive.

Konfiguracja serwera

Ustaw następujące ustawienia:

  • ClientTimeoutInterval (ustawienie domyślne: 30 sekund): klienci przedziału czasu muszą wysłać komunikat, zanim serwer zamknie połączenie.
  • HandshakeTimeout (ustawienie domyślne: 15 sekund): interwał używany przez serwer do przekroczenia limitu czasu przychodzących żądań uzgadniania przez klientów.
  • KeepAliveInterval (wartość domyślna: 15 sekund): interwał używany przez serwer do wysyłania utrzymywania aktywności poleceń ping do połączonych klientów. Należy pamiętać, że istnieje również ustawienie interwału Keep-Alive na kliencie, które powinno być zgodne z wartością serwera.

Element ClientTimeoutInterval i HandshakeTimeout można zwiększyć, a element KeepAliveInterval może pozostać taki sam. Należy pamiętać, że jeśli zmienisz wartości, upewnij się, że limity czasu są co najmniej dwukrotnie równe interwałowi Keep-Alive i że interwał Keep-Alive jest zgodny między serwerem a klientem. Aby uzyskać więcej informacji, zobacz sekcję Konfigurowanie SignalR limitów czasu i funkcji Keep-Alive na kliencie .

W poniższym przykładzie:

  • Wartość ClientTimeoutInterval jest zwiększana do 60 sekund (wartość domyślna: 30 sekund).
  • Wartość HandshakeTimeout jest zwiększana do 30 sekund (wartość domyślna: 15 sekund).
  • Parametr KeepAliveInterval nie jest ustawiony w kodzie dewelopera i używa jego wartości domyślnej 15 sekund. Zmniejszenie wartości interwału Keep-Alive zwiększa częstotliwość wysyłania poleceń ping do komunikacji, co zwiększa obciążenie aplikacji, serwera i sieci. Należy zachować ostrożność, aby uniknąć wprowadzania niskiej wydajności podczas obniżania interwału Keep-Alive.

Blazor Web App (.NET 8 lub nowszy) w pliku projektu Program serwera:

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

Blazor ServerProgram w pliku:

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

Aby uzyskać więcej informacji, zobacz sekcję Opcje obsługi obwodu po stronie serwera.

Konfiguracja klientów

Ustaw następujące ustawienia:

  • withServerTimeout (wartość domyślna: 30 sekund): konfiguruje limit czasu serwera określony w milisekundach dla połączenia koncentratora obwodu.
  • withKeepAliveInterval (wartość domyślna: 15 sekund): interwał określony w milisekundach, w którym połączenie wysyła komunikaty Keep-Alive.

Limit czasu serwera można zwiększyć, a interwał Keep-Alive może pozostać taki sam. Należy pamiętać, że jeśli zmienisz wartości, upewnij się, że limit czasu serwera jest co najmniej dwukrotnie większy niż wartość interwału Keep-Alive i że wartości interwału Keep-Alive są zgodne między serwerem a klientem. Aby uzyskać więcej informacji, zobacz sekcję Konfigurowanie SignalR limitów czasu i funkcji Keep-Alive na kliencie .

W poniższym przykładzie konfiguracji uruchamiania (lokalizacja skryptuBlazor) dla limitu czasu serwera jest używana niestandardowa wartość 60 sekund. Interwał Keep-Alive (withKeepAliveInterval) nie jest ustawiony i używa wartości domyślnej 15 sekund.

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>

Podczas tworzenia połączenia koncentratora w składniku ustaw limit czasu serwera (WithServerTimeoutdomyślnie: 30 sekund) na .HubConnectionBuilder Ustaw wartość (domyślną HandshakeTimeout : 15 sekund) na skompilowanych HubConnectionelementach . Upewnij się, że limity czasu są co najmniej dwukrotnie równe interwałowi Keep-Alive (WithKeepAliveInterval/KeepAliveInterval) i że wartość Keep-Alive jest zgodna między serwerem a klientem.

Poniższy przykład jest oparty na składniku Index w samouczku z samouczkiemSignalRBlazor. Limit czasu serwera jest zwiększany do 60 sekund, a limit czasu uzgadniania jest zwiększany do 30 sekund. Interwał Keep-Alive nie jest ustawiony i używa wartości domyślnej 15 sekund.

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();
}

Ustaw następujące ustawienia:

  • serverTimeoutInMilliseconds (wartość domyślna: 30 sekund): konfiguruje limit czasu serwera określony w milisekundach dla połączenia koncentratora obwodu.
  • keepAliveIntervalInMilliseconds (wartość domyślna: 15 sekund): interwał określony w milisekundach, w którym połączenie wysyła komunikaty Keep-Alive.

Limit czasu serwera można zwiększyć, a interwał Keep-Alive może pozostać taki sam. Należy pamiętać, że jeśli zmienisz wartości, upewnij się, że limit czasu serwera jest co najmniej dwukrotnie większy niż wartość interwału Keep-Alive i że wartości interwału Keep-Alive są zgodne między serwerem a klientem. Aby uzyskać więcej informacji, zobacz sekcję Konfigurowanie SignalR limitów czasu i funkcji Keep-Alive na kliencie .

W poniższym przykładzie konfiguracji uruchamiania (lokalizacja skryptuBlazor) dla limitu czasu serwera jest używana niestandardowa wartość 60 sekund. Interwał Keep-Alive (keepAliveIntervalInMilliseconds) nie jest ustawiony i używa wartości domyślnej 15 sekund.

W pliku 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>

Podczas tworzenia połączenia koncentratora w składniku ustaw ServerTimeout wartość (domyślną: 30 sekund) i HandshakeTimeout (wartość domyślna: 15 sekund) na skompilowanych HubConnectionelementach . Upewnij się, że limity czasu są co najmniej dwukrotnie równe interwałowi Keep-Alive. Upewnij się, że interwał Keep-Alive jest zgodny między serwerem a klientem.

Poniższy przykład jest oparty na składniku Index w samouczku z samouczkiemSignalRBlazor. Wartość ServerTimeout jest zwiększana do 60 sekund i HandshakeTimeout zwiększa się do 30 sekund. Interwał Keep-Alive (KeepAliveInterval) nie jest ustawiony i używa wartości domyślnej 15 sekund.

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();
}

SignalR Odłącz Blazorobwód od klienta

BlazorSignalR Obwód jest odłączony po wyzwoleniu unload zdarzenia strony. Aby rozłączyć obwód dla innych scenariuszy na kliencie, należy wywołać Blazor.disconnect w odpowiedniej procedurze obsługi zdarzeń. W poniższym przykładzie obwód jest odłączony, gdy strona jest ukryta (pagehide zdarzenie):

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

Aby uzyskać więcej informacji na temat uruchamiania platformy Blazor, zobacz Uruchamianie platformy ASP.NET Core Blazor.

Procedura obsługi obwodu po stronie serwera

Można zdefiniować procedurę obsługi obwodu, która umożliwia uruchamianie kodu na zmianach stanu obwodu użytkownika. Procedura obsługi obwodów jest implementowana przez wyprowadzanie CircuitHandler i rejestrowanie klasy w kontenerze usługi aplikacji. Poniższy przykład procedury obsługi obwodów śledzi otwarte SignalR połączenia.

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;
}

Programy obsługi obwodów są rejestrowane przy użyciu di. Wystąpienia o określonym zakresie są tworzone na wystąpienie obwodu. TrackingCircuitHandler Korzystając z powyższego przykładu, jest tworzona pojedyncza usługa, ponieważ stan wszystkich obwodów musi być śledzony.

W pliku Program:

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

W Startup.ConfigureServices pliku :Startup.cs

services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

Jeśli metody niestandardowego programu obsługi obwodu zgłaszają nieobsługiwany wyjątek, wyjątek jest krytyczny dla obwodu. Aby tolerować wyjątki w kodzie programu obsługi lub wywoływanych metodach, opakuj kod w co najmniej jednej try-catch instrukcji z obsługą błędów i rejestrowaniem.

Gdy obwód kończy się, ponieważ użytkownik rozłączył się, a struktura czyści stan obwodu, struktura usuwa zakres di obwodu. Usuwanie zakresu usuwa wszystkie usługi DI o zakresie obwodu, które implementują System.IDisposable. Jeśli jakakolwiek usługa DI zgłasza nieobsługiwany wyjątek podczas usuwania, struktura rejestruje wyjątek. Aby uzyskać więcej informacji, zobacz ASP.NET Core dependency injection (Wstrzykiwanie zależności ASP.NET CoreBlazor).

Procedura obsługi obwodu po stronie serwera w celu przechwytywania użytkowników dla usług niestandardowych

Użyj elementu , CircuitHandler aby przechwycić użytkownika z AuthenticationStateProvider elementu i ustawić tego użytkownika w usłudze. Aby uzyskać więcej informacji i przykładowy kod, zobacz ASP.NET Podstawowe scenariusze zabezpieczeń i Blazor Web App dodatkowe scenariusze zabezpieczeń.

Zamykanie obwodów, gdy nie ma pozostałych składników programu Interactive Server

Składniki interaktywnego serwera obsługują zdarzenia internetowego interfejsu użytkownika przy użyciu połączenia w czasie rzeczywistym z przeglądarką nazywaną obwodem. Obwód i skojarzony z nim stan są tworzone, gdy jest renderowany główny składnik interactive server. Obwód jest zamykany, gdy na stronie nie ma żadnych pozostałych składników serwera interakcyjnego, co zwalnia zasoby serwera.

IHttpContextAccessor/HttpContext w Razor składnikach

IHttpContextAccessor należy unikać renderowania interakcyjnego, ponieważ nie ma prawidłowej HttpContext dostępności.

IHttpContextAccessor Może służyć do składników, które są statycznie renderowane na serwerze. Zalecamy jednak unikanie go, jeśli to możliwe.

HttpContextMoże być używany jako parametr kaskadowy tylko w statycznie renderowanych składnikach głównych dla zadań ogólnych, takich jak inspekcja i modyfikowanie nagłówków lub innych właściwości w składniku App (Components/App.razor). Wartość jest zawsze null dla renderowania interakcyjnego.

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

W przypadku scenariuszy, w których HttpContext element jest wymagany w składnikach interaktywnych, zalecamy przepływ danych za pośrednictwem stanu trwałego składnika z serwera. Aby uzyskać więcej informacji, zobacz scenariusze ASP.NET Core po stronie serwera i Blazor Web App dodatkowe scenariusze zabezpieczeń.

Nie używaj IHttpContextAccessor/HttpContext bezpośrednio ani pośrednio składników Razor aplikacji po stronie Blazor serwera. Blazor aplikacje działają poza kontekstem potoku ASP.NET Core. Nie HttpContext ma gwarancji, że element jest dostępny w programie IHttpContextAccessori HttpContext nie ma gwarancji, że przechowuje kontekst, w ramach którego uruchomiono aplikację Blazor .

Zalecaną metodą przekazywania stanu żądania do Blazor aplikacji są parametry składnika głównego podczas początkowego renderowania aplikacji. Alternatywnie aplikacja może skopiować dane do usługi o określonym zakresie w zdarzeniu cyklu życia inicjowania składnika głównego do użycia w całej aplikacji. Aby uzyskać więcej informacji, zobacz scenariusze ASP.NET Core po stronie serwera i Blazor Web App dodatkowe scenariusze zabezpieczeń.

Krytycznym aspektem zabezpieczeń po stronie Blazor serwera jest to, że użytkownik dołączony do danego obwodu może zostać zaktualizowany w pewnym momencie po ustanowieniu obwoduBlazor, ale IHttpContextAccessornie został zaktualizowany. Aby uzyskać więcej informacji na temat rozwiązywania tej sytuacji z usługami niestandardowymi, zobacz ASP.NET Podstawowe scenariusze zabezpieczeń i Blazor Web App dodatkowe scenariusze zabezpieczeń.

Dodatkowe zasoby po stronie serwera