Teilen über


ASP.NET Core-Blazor-SignalR-Leitfaden

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird das Konfigurieren und Verwalten von SignalR Verbindungen in Blazor-Apps erläutert.

Allgemeine Leitfäden zur Konfiguration von ASP.NET Core SignalR finden Sie in den Themen im Bereich Übersicht über ASP.NET Core SignalR der Dokumentation, insbesondere zur Konfiguration von ASP.NET Core SignalR.

Serverseitige Apps verwenden ASP.NET Core SignalR für die Kommunikation mit dem Browser. Die Hosting- und Skalierungsbedingungen von SignalR gelten für serverseitige Apps.

Blazor funktioniert am besten, wenn WebSockets aufgrund geringerer Latenz und wegen Zuverlässigkeit und Sicherheit zum SignalR-Transport verwendet wird. SignalR verwendet Long Polling, wenn WebSockets nicht verfügbar oder die App explizit für die Verwendung von Long Polling konfiguriert ist.

Azure SignalR Service mit zustandsbehafteter Verbindungswiederherstellung

Die zustandsbehaftete Verbindungswiederherstellung (WithStatefulReconnect) wurde mit .NET 8 veröffentlicht, wird jedoch für den Azure SignalR Service derzeit nicht unterstützt. Weitere Informationen finden Sie unter Stateful Reconnect Support? (Azure/azure-signalr Nr. 1878).

WebSocket-Komprimierung für interaktive Serverkomponenten

Standardmäßig sind interaktive Serverkomponenten:

  • Aktivieren sie die Komprimierung für WebSocket-Verbindungen. ConfigureWebsocketOptions steuert die WebSocket-Komprimierung.

  • Führen Sie eine frame-ancestors-Anweisung für die Richtlinie für Inhaltssicherheit (Content Security Policy, CSP) ein, die auf 'self' festgelegt ist und die Einbettung der App nur in einem <iframe> des Ursprungs zulässt, über den die App verarbeitet wird, wenn die Komprimierung aktiviert ist oder eine Konfiguration für den WebSocket vorhanden ist. ContentSecurityFrameAncestorPolicy steuert den frame-ancestors CSP.

Der frame-ancestors-CSP kann manuell entfernt werden, indem der Wert von ConfigureWebSocketOptions auf null gesetzt wird, da Sie den CSP möglicherweise zentral konfigurieren möchten. Wenn der frame-ancestors-CSP zentral verwaltet wird, müssen Sie darauf achten, eine Richtlinie anzuwenden, wenn das erste Dokument gerendert wird. Es wird nicht empfohlen, die Richtlinie vollständig zu entfernen, da sie die App anfällig für Angriffe machen könnte.

Anwendungsbeispiele:

Deaktivieren Sie die Komprimierung durch Festlegen ConfigureWebSocketOptions auf null, wodurch die Sicherheitsanfälligkeit der App für Angriffe reduziert wird, dies kann jedoch zu einer verringerten Leistung führen:

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

Wenn die Komprimierung aktiviert ist, konfigurieren Sie einen strikteren frame-ancestors CSP mit einem Wert von 'none' (einzelne Anführungszeichen erforderlich), wodurch die WebSocket-Komprimierung ermöglicht wird, aber verhindert, dass Browser die App in eine der <iframe>folgenden Komponenten einbetten:

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

Wenn die Komprimierung aktiviert ist, entfernen Sie den frame-ancestors CSP durch Festlegen ContentSecurityFrameAncestorsPolicy auf null. Dieses Szenario wird nur für Apps empfohlen, die den CSP zentralfestlegen:

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

Wichtig

Browser wenden CSP-Direktiven aus mehreren CSP-Headern mithilfe des strengsten Richtliniendirektivenwerts an. Daher kann ein Entwickler keine schwächere frame-ancestors Richtlinie hinzufügen, als 'self' absichtlich oder versehentlich.

Einfache Anführungszeichen sind für den an ContentSecurityFrameAncestorsPolicy übergebenen String-Wert erforderlich:

Nicht unterstützte Werte: none, self

Unterstützte Werte: 'none', 'self'

Weitere Optionen sind die Angabe einer oder mehrerer Hostquellen und Schemaquellen.

Informationen zu den Auswirkungen auf die Sicherheit finden Sie unter Anleitung zur Bedrohungsabwehr für ASP.NET CoreBlazor interaktives serverseitiges Rendering . Weitere Informationen zur Direktive finden Sie in der CSP: (MDN-Dokumentation).For more information on the frame-ancestors directive, see CSP: frame-ancestors (MDN documentation).

Deaktivieren der Middlewaredienste für die Komprimierung von Antworten für Hot Reload

Deaktivieren Sie die Middlewaredienste für die Komprimierung von Antworten in der Development-Umgebung, wenn Sie Hot Reload verwenden. Rufen Sie unabhängig davon, ob der Standardcode aus einer Projektvorlage verwendet wird oder nicht, immer zuerst UseResponseCompression in der Pipeline für die Anfrageverarbeitung auf.

Gehen Sie in der Program-Datei folgendermaßen vor:

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

Clientseitige ursprungsübergreifende SignalR-Aushandlung für die Authentifizierung

In diesem Abschnitt wird erläutert, wie Sie den SignalR zugrunde liegenden Client so konfigurieren, dass er Anmeldeinformationen wie Cookies oder HTTP-Authentifizierungs-Header sendet.

Verwenden Sie SetBrowserRequestCredentials, um Include für ursprungsübergreifende fetch-Anforderungen festzulegen.

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

Wenn eine Hubverbindung erstellt wird, weisen Sie den HttpMessageHandler der Option HttpMessageHandlerFactory zu:

private HubConnectionBuilder? hubConnection;

...

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

Im vorstehenden Beispiel wird die Hubverbindungs-URL mit der absoluten URI-Adresse auf /chathub konfiguriert. Der URI kann auch über eine Zeichenfolge wie https://signalr.example.com oder über die Konfiguration festgelegt werden. Navigation ist ein eingefügter NavigationManager.

Weitere Informationen finden Sie unter Konfiguration von SignalR in ASP.NET Core.

Clientseitiges Rendering

Wenn Vorabrendering konfiguriert ist, erfolgt dieses vor dem Herstellen der Clientverbindung mit dem Server. Weitere Informationen finden Sie unter Prerendering von Razor-Komponenten in ASP.NET Core.

Wenn Vorabrendering konfiguriert ist, erfolgt dieses vor dem Herstellen der Clientverbindung mit dem Server. Weitere Informationen finden Sie in den folgenden Artikeln:

Vorab gerenderte Zustandsgröße und SignalR-Grenzwert für die Nachrichtengröße

Eine große vorab gerenderte Zustandsgröße kann den Grenzwert für die Nachrichtengröße der SignalR-Verbindung überschreiten, was Folgendes zur Folge hat:

  • Die SignalR-Verbindung wird mit einem Fehler auf dem Client nicht initialisiert: Circuit host not initialized.
  • Die Benutzeroberfläche für eine erneute Verbindung auf dem Client wird angezeigt, wenn die Verbindung fehlschlägt. Eine Wiederherstellung ist nicht möglich.

Verwenden Sie einen der folgenden Ansätze, um das Problem zu beheben:

  • Verringern Sie die Menge der Daten, die Sie in den vorab gerenderten Zustand versetzen.
  • Erhöhen Sie den SignalR-Grenzwert für die Nachrichtengröße. WARNUNG: Das Erhöhen des Grenzwerts kann das Risiko von Denial-of-Service-Angriffen (DoS) erhöhen.

Zusätzliche clientseitige Ressourcen

Verwenden von Sitzungsaffinität (persistentener Sitzungen) für serverseitiges Webfarmhosting

Wenn mehr als ein Back-End-Server verwendet wird, muss die Anwendung eine Sitzungsaffinität implementieren, auch sticky sessions genannt. Die Sitzungsaffinität stellt sicher, dass die Verbindung eines Clients mit demselben Server wiederhergestellt wird, wenn die Verbindung unterbrochen wird, was wichtig ist, da der Client-Status nur im Speicher des Servers gespeichert wird, der die Client-Verbindung zuerst aufgebaut hat.

Der folgende Fehler wird von einer App ausgelöst, die keine Sitzungsaffinität in einer Webfarm aktiviert hat:

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

Weitere Informationen zur Sitzungsaffinität beim Hosting mit Azure App Service finden Sie unter Hosten und Bereitstellen von serverseitigen ASP.NET Core-Blazor-Apps.

Azure SignalR Service

Der optionale Azure SignalR Service arbeitet mit dem Hub der App SignalR zusammen, um eine serverseitige App auf eine große Anzahl gleichzeitiger Verbindungen zu skalieren. Außerdem tragen die globale Reichweite und die Hochleistungsrechenzentren von Service erheblich zur Verringerung der geografiebedingten Latenz bei.

Der Dienst ist nicht erforderlich für Blazor-Apps, die in Azure App Service oder Azure Container Apps gehostet werden, kann aber in anderen Hosting-Umgebungen hilfreich sein:

  • Um das Skalieren der Verbindung zu erleichtern.
  • Um die globale Verteilung zu gewährleisten.

Weitere Informationen finden Sie unter Hosten und Bereitstellen von serverseitigen ASP.NET Core Blazor-Apps.

Optionen für serverseitige Verbindungshandler

Konfigurieren Sie die Leitung mit CircuitOptions. Zeigen Sie Standardwerte in der Referenzquelle an.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Lesen Sie die Optionen in der Program-Datei mit einem Optionsdelegat zu AddInteractiveServerComponents oder legen Sie sie fest. Der {OPTION}-Platzhalter stellt die Option dar, und der {VALUE}-Platzhalter ist der Wert.

Gehen Sie in der Program-Datei folgendermaßen vor:

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

Lesen Sie die Optionen in der Program-Datei mit einem Optionsdelegat zu AddServerSideBlazor oder legen Sie sie fest. Der {OPTION}-Platzhalter stellt die Option dar, und der {VALUE}-Platzhalter ist der Wert.

Gehen Sie in der Program-Datei folgendermaßen vor:

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

Lesen Sie die Optionen in Startup.ConfigureServices mit einem Optionsdelegat zu AddServerSideBlazor oder legen Sie sie fest. Der {OPTION}-Platzhalter stellt die Option dar, und der {VALUE}-Platzhalter ist der Wert.

In Startup.ConfigureServices von Startup.cs:

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

Verwenden Sie HubConnectionContextOptions mit AddHubOptions, um HubConnectionContext zu konfigurieren. Zeigen Sie die Standardwerte für die Hubverbindungskontextoptionen in der Referenzquellean. Optionsbeschreibungen in der SignalR-Dokumentation finden Sie unter ASP.NET Core SignalR Konfiguration. Der {OPTION}-Platzhalter stellt die Option dar, und der {VALUE}-Platzhalter ist der Wert.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Gehen Sie in der Program-Datei folgendermaßen vor:

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

Gehen Sie in der Program-Datei folgendermaßen vor:

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

In Startup.ConfigureServices von Startup.cs:

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

Warnung

Der Standardwert von MaximumReceiveMessageSize ist 32 KB. Das Erhöhen des Werts kann das Risiko von Denial-of-Service-Angriffen (DoS) erhöhen.

Blazor setzt voraus, dass MaximumParallelInvocationsPerClient auf 1 (Standardwert) festgelegt ist. Weitere Informationen finden Sie unter MaximumParallelInvocationsPerClient > 1 unterbricht den Dateiupload im Blazor Server-Modus (dotnet/aspnetcore #53951).

Informationen zur Arbeitsspeicherverwaltung finden Sie unter Hosten und Bereitstellen von serverseitigen ASP.NET Core Blazor-Apps.

Blazor Huboptionen

Konfigurieren Sie MapBlazorHub-Optionen zum Steuern von HttpConnectionDispatcherOptions des Blazor-Hubs. Zeigen Sie die Standardwerte für die Hubverbindungsverteileroptionen in der Referenzquelle an. Der {OPTION}-Platzhalter stellt die Option dar, und der {VALUE}-Platzhalter ist der Wert.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Platzieren Sie den Aufruf nach dem Aufruf app.MapBlazorHub app.MapRazorComponents in der App-Datei Program :

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

Die Konfiguration des von AddInteractiveServerRenderMode mit MapBlazorHub verwendeten Hubs schlägt mit einem AmbiguousMatchException fehl:

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

Um das Problem für Apps, die auf .NET 8 abzielen, zu umgehen, geben Sie dem benutzerdefiniert konfigurierten Blazor-Hub mit der WithOrder-Methode eine höhere Priorität:

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

Weitere Informationen finden Sie in den folgenden Ressourcen:

Geben Sie die Optionen app.MapBlazorHub für die App-Datei Program an:

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

Geben Sie die Optionen für die Endpunktroutingkonfiguration an app.MapBlazorHub:

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

Maximale Größe für empfangene Nachrichten

Dieser Abschnitt gilt nur für Projekte, die SignalR implementieren.

Die für Hubmethoden zulässige maximale Größe eingehender SignalR-Nachrichten ist durch HubOptions.MaximumReceiveMessageSize begrenzt (Standard: 32 KB). SignalR-Nachrichten, die größer als MaximumReceiveMessageSize sind, lösen einen Fehler aus. Das Framework beinhaltet keine Beschränkungen hinsichtlich der Größe einer SignalR-Nachricht vom Hub an einen Client.

Wenn die SignalR-Protokollierung nicht auf SignalR oder Überwachung festgelegt ist, wird ein Fehler zur Nachrichtengröße nur in der Konsole für Entwicklertools des Browsers angezeigt:

Fehler: Die Verbindung wurde mit dem folgenden Fehler getrennt: „Fehler: Der Server hat beim Schließen einen Fehler zurückgegeben: Die Verbindung wurde mit einem Fehler geschlossen.“

Wenn die serverseitige Protokollierung in SignalR auf Debuggen oder Überwachung festgelegt ist, tritt bei der serverseitigen Protokollierung eine InvalidDataException-Ausnahme für einen Fehler in Bezug auf die Nachrichtengröße auf.

appsettings.Development.json:

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

Error:

System.IO.InvalidDataException: The maximum message size of 32768B was exceeded. The message size can be configured in AddHubOptions. (System.IO.InvalidDataException: Die maximale Nachrichtengröße von 32.768 Byte wurde überschritten. Die Nachrichtengröße kann unter AddHubOptions konfiguriert werden.)

Ein Ansatz besteht darin, den Grenzwert zu erhöhen, indem MaximumReceiveMessageSize in der Program-Datei festgelegt wird. Im folgenden Beispiel wird die maximale Größe eingehender Nachrichten auf 64 KB festgelegt:

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

Die Erhöhung des Grenzwerts für die Größe eingehender SignalR-Nachrichten geht zu Lasten der Anforderung von mehr Serverressourcen und erhöht das Risiko von Denial-of-Service-(DoS-)Angriffen. Darüber hinaus kann das Lesen sehr großer Inhalte in den Arbeitsspeicher als Zeichenfolgen oder Bytearrays zu Zuordnungen führen, die vom Garbage Collector nur schlecht verarbeitet werden können. Dies kann zu zusätzlichen Leistungseinbußen führen.

Eine bessere Möglichkeit zum Lesen großer Mengen an Nutzdaten besteht darin, den Inhalt in kleineren Blöcken zu senden und die Nutzdaten als Stream zu verarbeiten. Dies kann verwendet werden, wenn große JavaScript (JS) Interop-JSON-Nutzdaten gelesen werden oder wenn JS Interop-Daten als Rohbytes verfügbar sind. Ein Beispiel für das Senden großer binärer Nutzdaten in serverseitigen Apps, bei dem ähnliche Techniken wie die InputFile-Komponente verwendet werden, finden Sie in der BinarySubmit-Beispiel-App und im BlazorInputLargeTextArea-Komponentenbeispiel.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Formulare, die große Nutzlasten über SignalR verarbeiten, können Sie die JS-Streaminginteroperabilität auch direkt verwenden. Weitere Informationen finden Sie unter Aufrufen von .NET-Methoden über JavaScript-Funktionen in ASP.NET Core Blazor. Ein Formularbeispiel, das <textarea>-Daten auf den Server streamt, finden Sie unter Problembehandlung für ASP.NET Core Blazor Formulare.

Ein Ansatz besteht darin, den Grenzwert zu erhöhen, indem MaximumReceiveMessageSize in der Program-Datei festgelegt wird. Im folgenden Beispiel wird die maximale Größe eingehender Nachrichten auf 64 KB festgelegt:

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

Die Erhöhung des Grenzwerts für die Größe eingehender SignalR-Nachrichten geht zu Lasten der Anforderung von mehr Serverressourcen und erhöht das Risiko von Denial-of-Service-(DoS-)Angriffen. Darüber hinaus kann das Lesen sehr großer Inhalte in den Arbeitsspeicher als Zeichenfolgen oder Bytearrays zu Zuordnungen führen, die vom Garbage Collector nur schlecht verarbeitet werden können. Dies kann zu zusätzlichen Leistungseinbußen führen.

Eine bessere Möglichkeit zum Lesen großer Mengen an Nutzdaten besteht darin, den Inhalt in kleineren Blöcken zu senden und die Nutzdaten als Stream zu verarbeiten. Dies kann verwendet werden, wenn große JavaScript (JS) Interop-JSON-Nutzdaten gelesen werden oder wenn JS Interop-Daten als Rohbytes verfügbar sind. Ein Beispiel für das Senden großer binärer Payloads in Blazor Server, bei dem ähnliche Techniken wie die InputFile-Komponente verwendet werden, finden Sie in der Beispiel-App BinarySubmit und im BlazorInputLargeTextArea-Komponentenbeispiel.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Formulare, die große Nutzlasten über SignalR verarbeiten, können Sie die JS-Streaminginteroperabilität auch direkt verwenden. Weitere Informationen finden Sie unter Aufrufen von .NET-Methoden über JavaScript-Funktionen in ASP.NET Core Blazor. Ein Formularbeispiel zum Streamen von <textarea>-Daten in einer Blazor Server-App finden Sie unter Problembehandlung für ASP.NET Core Blazor Formulare.

Erhöhen Sie den Grenzwert, indem Sie MaximumReceiveMessageSize in Startup.ConfigureServices festlegen.

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

Die Erhöhung des Grenzwerts für die Größe eingehender SignalR-Nachrichten geht zu Lasten der Anforderung von mehr Serverressourcen und erhöht das Risiko von Denial-of-Service-(DoS-)Angriffen. Darüber hinaus kann das Lesen sehr großer Inhalte in den Arbeitsspeicher als Zeichenfolgen oder Bytearrays zu Zuordnungen führen, die vom Garbage Collector nur schlecht verarbeitet werden können. Dies kann zu zusätzlichen Leistungseinbußen führen.

Beachten Sie die folgenden Anleitungen, wenn Sie Code zum Übertragen großer Datenmengen entwickeln:

  • Nutzen Sie zur Übertragung von Daten, deren Größe die Begrenzung für eingehende SignalR-Nachrichten überschreitet, die Unterstützung der nativen JS-Streaminginteroperabilität.
  • Allgemeine Tipps:
    • Ordnen Sie in JS- und C#-Code keine großen Objekte zu.
    • Geben Sie belegten Arbeitsspeicher frei, wenn der Prozess beendet oder abgebrochen wird.
    • Erzwingen Sie die folgenden zusätzlichen Anforderungen aus Sicherheitsgründen:
      • Deklarieren Sie die maximale Datei- oder Datengröße, die übermittelt werden kann.
      • Deklarieren Sie die minimale Uploadrate vom Client an den Server.
    • Nachdem die Daten vom Server empfangen wurden, ist mit den Daten Folgendes möglich:
      • Sie können temporär in einem Speicherpuffer gespeichert werden, bis alle Segmente gesammelt wurden.
      • Sie können sofort verarbeitet werden. Beispielsweise können die Daten sofort in einer Datenbank gespeichert oder auf den Datenträger geschrieben werden, wenn die einzelnen Segmente empfangen werden.
  • Segmentieren Sie die Daten in kleinere Teile, und senden Sie die Datensegmente sequenziell, bis alle Daten vom Server empfangen wurden.
  • Ordnen Sie in JS- und C#-Code keine großen Objekte zu.
  • Blockieren Sie den hauptsächlichen Benutzeroberflächenthread nicht für lange Zeiträume, wenn Sie Daten senden oder empfangen.
  • Geben Sie belegten Arbeitsspeicher frei, wenn der Prozess beendet oder abgebrochen wird.
  • Erzwingen Sie die folgenden zusätzlichen Anforderungen aus Sicherheitsgründen:
    • Deklarieren Sie die maximale Datei- oder Datengröße, die übermittelt werden kann.
    • Deklarieren Sie die minimale Uploadrate vom Client an den Server.
  • Nachdem die Daten vom Server empfangen wurden, ist mit den Daten Folgendes möglich:
    • Sie können temporär in einem Speicherpuffer gespeichert werden, bis alle Segmente gesammelt wurden.
    • Sie können sofort verarbeitet werden. Beispielsweise können die Daten sofort in einer Datenbank gespeichert oder auf den Datenträger geschrieben werden, wenn die einzelnen Segmente empfangen werden.

Routenkonfiguration für den serverseitigen Blazor-Hubendpunkt

Rufen Sie in der Program-Datei MapBlazorHub auf, um den Blazor-Hub dem Standardpfad der App zuzuordnen. Das Blazor-Skript (blazor.*.js) verweist automatisch auf den durch MapBlazorHub erstellten Endpunkt.

Reflektieren des serverseitigen Verbindungsstatus auf der Benutzeroberfläche

Wenn der Client erkennt, dass keine Verbindung mehr besteht, wird dem Benutzer eine Standardbenutzeroberfläche angezeigt, während der Client versucht, eine neue Verbindung herzustellen. Wenn die Wiederherstellung der Verbindung fehlschlägt, wird dem Benutzer die Option angezeigt, es noch mal zu versuchen.

Wenn Sie die Benutzeroberfläche anpassen möchten, definieren Sie ein einzelnes Element mit einem id von components-reconnect-modal. Im folgenden Beispiel wird das Element in der App-Komponente platziert.

App.razor:

Wenn Sie die Benutzeroberfläche anpassen möchten, definieren Sie ein einzelnes Element mit einem id von components-reconnect-modal. Im folgenden Beispiel wird das Element auf der Hostseite platziert.

Pages/_Host.cshtml:

Wenn Sie die Benutzeroberfläche anpassen möchten, definieren Sie ein einzelnes Element mit einem id von components-reconnect-modal. Im folgenden Beispiel wird das Element auf der Layoutseite platziert.

Pages/_Layout.cshtml:

Wenn Sie die Benutzeroberfläche anpassen möchten, definieren Sie ein einzelnes Element mit einem id von components-reconnect-modal. Im folgenden Beispiel wird das Element auf der Hostseite platziert.

Pages/_Host.cshtml:

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

Hinweis

Wenn mehrere Elemente mit einem id von components-reconnect-modal von der App gerendert werden, empfängt nur das erste gerenderte Element CSS-Klassenänderungen, um das Element anzuzeigen oder auszublenden.

Fügen Sie dem Stylesheet der Website die folgenden CSS-Stile hinzu.

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

In der folgenden Tabelle werden die CSS-Klassen beschrieben, die vom Blazor-Framework auf das components-reconnect-modal-Element angewendet werden.

CSS-Klasse Bedeutung
components-reconnect-show Die Verbindung wurde getrennt. Der Client versucht, die Verbindung wiederherzustellen. Die modale Seite wird angezeigt.
components-reconnect-hide Auf dem Server wird eine aktive Verbindung wiederhergestellt. Die modale Seite wird ausgeblendet.
components-reconnect-failed Die Wiederherstellung der Verbindung ist wahrscheinlich aufgrund eines Netzwerkfehlers fehlgeschlagen. Zum erneuten Herstellen der Verbindung rufen Sie window.Blazor.reconnect() in JavaScript auf.
components-reconnect-rejected Die Wiederherstellung der Verbindung wurde abgelehnt. Der Server wurde zwar erreicht, jedoch hat dieser die Verbindung verweigert. Der Status des Benutzers auf dem Server wurde verworfen. Rufen Sie location.reload() in JavaScript auf, um die App neu zu laden. Dieser Verbindungszustand kann aus folgenden Gründen auftreten:
  • Aufgetretener Absturz auf der serverseitigen Verbindung.
  • Der Client war lange nicht verbunden, sodass der Server den Benutzerstatus verworfen hat. Instanzen der Komponenten des Benutzers werden verworfen.
  • Der Server wird neu gestartet, oder der Workerprozess der App wird wiederverwendet.

Passen Sie die Verzögerung an, bevor die Benutzeroberfläche einer erneuten Verbindungsherstellung eingeblendet wird, indem Sie die transition-delay-Eigenschaft im CSS der Website für das modale Element festlegen. Im folgenden Beispiel wird die Übergangsverzögerung von 500 ms (Standard) auf 1.000 ms (1 Sekunde) festgelegt.

wwwroot/app.css:

wwwroot/css/site.css:

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

Um den aktuellen erneuten Verbindungsversuch anzuzeigen, definieren Sie ein Element mit einer id mit dem Wert components-reconnect-current-attempt. Um die maximale Anzahl von Wiederholungsversuchen für eine erneute Verbindung anzuzeigen, definieren Sie ein Element mit einer id mit dem Wert components-reconnect-max-retries. Im folgenden Beispiel werden diese Elemente gemäß dem vorherigen Beispiel in einem modalen Element für den erneuten Verbindungsversuch platziert.

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

Wenn das modale Element für die erneute Verbindung angezeigt wird, wird basierend auf dem vorherigen Code der Inhalt ähnlich wie im folgenden Beispiel gerendert:

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

Serverseitiges Rendering

Standardmäßig werden Komponenten auf dem Server schon vor dem Herstellen der Clientverbindung mit dem Server vorab gerendert. Weitere Informationen finden Sie unter Prerendering von Razor-Komponenten in ASP.NET Core.

Standardmäßig werden Komponenten auf dem Server schon vor dem Herstellen der Clientverbindung mit dem Server vorab gerendert. Weitere Informationen finden Sie unter Taghilfsprogramm für Komponenten in ASP.NET Core.

Überwachen der Aktivität der serverseitigen Verbindung

Überwachen Sie die Aktivität der eingehenden Verbindung mithilfe der CreateInboundActivityHandler-Methode für CircuitHandler. Eingehende Leitungsaktivität ist jede Aktivität, die vom Browser an den Server gesendet wird, z. B. Benutzeroberflächenereignisse oder Interop-Aufrufe von JavaScript an .NET.

Sie können beispielsweise einen Leitungsaktivitätshandler verwenden, um zu erkennen, ob sich der Client im Leerlauf befindet und seine Leitungs-ID protokollieren (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;
    }
}

Registrieren Sie in den Dienst in der Datei Program. Im folgenden Beispiel wird der standardmäßige Leerlauf-Timeout von fünf Minuten auf fünf Sekunden geändert, um die vorherige IdleCircuitHandler-Implementierung zu testen:

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

Leitungsaktivitätshandler bieten auch einen Ansatz für den Zugriff auf bereichsbezogene Blazor-Dienste aus anderen, Blazor-fremden DI-Bereichen (Dependency Injection, Abhängigkeitsinjektion). Weitere Informationen und Beispiele finden Sie hier:

Blazor Startup

Konfigurieren Sie den manuellen Start des Blazor-Schaltkreises einer SignalR-App in der App.razor-Datei eines Blazor Web App:

Konfigurieren des manuellen Starts der SignalR-Verbindung einer Blazor-App in der Datei Pages/_Host.cshtml (Blazor Server):

Konfigurieren des manuellen Starts der SignalR-Verbindung einer Blazor-App in der Datei Pages/_Layout.cshtml (Blazor Server):

Konfigurieren des manuellen Starts der SignalR-Verbindung einer Blazor-App in der Datei Pages/_Host.cshtml (Blazor Server):

  • Fügen Sie ein autostart="false"-Attribut zum <script>-Tag für das blazor.*.js-Skript hinzu.
  • Platzieren Sie ein Skript, das Blazor.start() aufruft, nach dem Laden des Blazor-Skripts und innerhalb des schließenden </body>-Tags.

Wenn autostart deaktiviert ist, funktionieren alle Aspekte der App, die nicht von der Verbindung abhängen, normal. So ist beispielsweise das clientseitige Routing betriebsbereit. Aspekte, die von der Verbindung abhängen, sind jedoch erst betriebsbereit, nachdem Blazor.start() aufgerufen wurde. Das App-Verhalten ist ohne eine bestehende Verbindung unvorhersehbar. Komponentenmethoden können beispielsweise nicht ausgeführt werden, solange die Verbindung getrennt ist.

Weitere Informationen einschließlich der zur Blazor-Initialisierung, wenn das Dokument bereit ist, und der Verkettung mit einer JS Promise finden Sie unter Starten von ASP.NET Core Blazor.

Konfigurieren von SignalR-Timeouts und Keep-Alive auf dem Client

Konfigurieren Sie die folgenden Werte für den Client:

  • withServerTimeout: konfiguriert das Servertimeout in Millisekunden. Wenn dieses Timeout abläuft, ohne eine Nachricht vom Server zu erhalten, wird die Verbindung mit einer Fehlermeldung beendet. Der Standard-Timeoutwert beträgt 30 Sekunden. Das Servertimeout sollte mindestens doppelt so groß sein wie der dem Keep-Alive-Intervall zugewiesene Wert (withKeepAliveInterval).
  • withKeepAliveInterval: konfiguriert das Keep-Alive-Intervall in Millisekunden (Standardintervall für das Pingen des Servers). Diese Einstellung ermöglicht dem Server, das Erkennen harter Verbindungsabbrüche, z. B. wenn ein Client seinen Computer vom Netzwerk trennt. Der Ping erfolgt höchstens so oft, wie der Server pingt. Wenn der Server alle fünf Sekunden pingt, weisen Sie ihm einen niedrigeren Wert als 5000 (5 Sekunden) zu. Der Standardwert ist 15 Sekunden. Das Keep-Alive-Intervall sollte kleiner oder gleich der Hälfte des Werts sein, der dem Servertimeout zugewiesen ist (withServerTimeout).

Das folgende Beispiel für die Datei App.razor (Blazor Web App) zeigt die Zuordnung von Standardwerten.

Blazor Web App:

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

Es folgt ein Beispiel für die Datei „Pages/_Host.cshtml“ (Blazor Server, alle Versionen außer ASP.NET Core in .NET 6) oder die Datei „Pages/_Layout.cshtml“ (Blazor Server, ASP.NET Core in .NET 6).

Blazor Server:

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

Im vorangegangenen Beispiel entspricht der {BLAZOR SCRIPT}-Platzhalter dem Pfad und dem Dateinamen des Blazor-Skripts. Den Speicherort des Skripts und den zu verwendenden Pfad finden Sie unter ASP.NET Core Blazor-Projektstruktur.

Wenn Sie eine Hubverbindung in einer Komponente erstellen, legen Sie ServerTimeout (Standardwert 30 Sekunden) und KeepAliveInterval (Standardwert 15 Sekunden) für den HubConnectionBuilder fest. Legen Sie die HandshakeTimeout (Standardeinstellung 15 Sekunden) für die erstellte HubConnection fest. Das folgende Beispiel veranschaulicht die Zuweisung von Standardwerten:

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

Konfigurieren Sie die folgenden Werte für den Client:

  • serverTimeoutInMilliseconds: das Servertimeout in Millisekunden. Wenn dieses Timeout abläuft, ohne eine Nachricht vom Server zu erhalten, wird die Verbindung mit einer Fehlermeldung beendet. Der Standard-Timeoutwert beträgt 30 Sekunden. Das Servertimeout sollte mindestens doppelt so groß sein wie der dem Keep-Alive-Intervall zugewiesene Wert (keepAliveIntervalInMilliseconds).
  • keepAliveIntervalInMilliseconds: Standardintervall für das Pingen des Servers. Diese Einstellung ermöglicht dem Server, das Erkennen harter Verbindungsabbrüche, z. B. wenn ein Client seinen Computer vom Netzwerk trennt. Der Ping erfolgt höchstens so oft, wie der Server pingt. Wenn der Server alle fünf Sekunden pingt, weisen Sie ihm einen niedrigeren Wert als 5000 (5 Sekunden) zu. Der Standardwert ist 15 Sekunden. Das Keep-Alive-Intervall sollte kleiner oder gleich der Hälfte des Werts sein, der dem Servertimeout zugewiesen ist (serverTimeoutInMilliseconds).

Es folgt ein Beispiel für die Datei „Pages/_Host.cshtml“ (Blazor Server, alle Versionen außer ASP.NET Core in .NET 6) oder die Datei „Pages/_Layout.cshtml“ (Blazor Server, ASP.NET Core in .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>

Im vorangegangenen Beispiel entspricht der {BLAZOR SCRIPT}-Platzhalter dem Pfad und dem Dateinamen des Blazor-Skripts. Den Speicherort des Skripts und den zu verwendenden Pfad finden Sie unter ASP.NET Core Blazor-Projektstruktur.

Wenn Sie eine Hubverbindung in einer Komponente erstellen, legen Sie ServerTimeout (Standardwert: 30 Sekunden), HandshakeTimeout (Standardwert: 15 Sekunden) und KeepAliveInterval (Standardwert: 15 Sekunden) für die erstellte HubConnection fest. Das folgende Beispiel veranschaulicht die Zuweisung von Standardwerten:

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

Beim Ändern der Werte für das Servertimeout (ServerTimeout) oder das Keep-Alive-Intervall (KeepAliveInterval):

  • Das Servertimeout sollte mindestens doppelt so groß sein wie der dem Keep-Alive-Intervall zugewiesene Wert.
  • Das Keep-Alive-Intervall sollte kleiner oder gleich der Hälfte des Werts sein, der dem Servertimeout zugewiesen ist.

Weitere Informationen finden Sie in den Abschnitten Globale Bereitstellung und Verbindungsfehler der folgenden Artikel:

Ändern des Handlers für die serverseitige Wiederherstellung einer Verbindung

Die Verbindungsereignisse des Handlers für die Wiederherstellung einer Verbindung können geändert werden, um benutzerdefinierte Verhaltensweisen zu erzeugen, z. B. für Folgendes:

  • Benachrichtigung an einen Benutzer, wenn die Verbindung unterbrochen wird
  • Ausführen der Protokollierung (vom Client), wenn eine Verbindung besteht

Zum Ändern der Verbindungsereignisse registrieren Sie Rückrufe für die folgenden Verbindungsänderungen:

  • Unterbrochene Verbindungen verwenden onConnectionDown.
  • Hergestellte/wieder hergestellte Verbindungen verwenden onConnectionUp.

Es müssen sowohl onConnectionDown als auch onConnectionUp angegeben werden.

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>

Im vorangegangenen Beispiel entspricht der {BLAZOR SCRIPT}-Platzhalter dem Pfad und dem Dateinamen des Blazor-Skripts. Den Speicherort des Skripts und den zu verwendenden Pfad finden Sie unter ASP.NET Core Blazor-Projektstruktur.

Automatisches Aktualisieren der Seite, wenn bei der serverseitigen Wiederherstellung der Verbindung ein Fehler auftritt

Beim Standardverhalten für das Erneuern von Verbindungen müssen die Benutzer*innen nach einem Fehler bei der erneuten Verbindungsherstellung eine manuelle Aktion zum Aktualisieren der Seite ausführen. Sie können jedoch auch einen benutzerdefinierten Verbindungshandler verwenden, um die Seite automatisch zu aktualisieren:

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>

Im vorangegangenen Beispiel entspricht der {BLAZOR SCRIPT}-Platzhalter dem Pfad und dem Dateinamen des Blazor-Skripts. Den Speicherort des Skripts und den zu verwendenden Pfad finden Sie unter ASP.NET Core Blazor-Projektstruktur.

Erstellen Sie die folgende wwwroot/boot.js-Datei.

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

Weitere Informationen zum Start von Blazor finden Sie unter Starten von ASP.NET Core Blazor.

Anpassen der Anzahl und des Intervalls für Wiederholungsversuche zum serverseitigen erneuten Herstellen einer Verbindung

Legen Sie zum Anpassen der Anzahl und des Intervalls der Wiederholungsversuche zum erneuten Herstellen einer Verbindung die zulässige Anzahl von Wiederholungsversuchen (maxRetries) und den zulässigen Zeitraum in Millisekunden (retryIntervalMilliseconds) für jeden Versuch fest.

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>

Im vorangegangenen Beispiel entspricht der {BLAZOR SCRIPT}-Platzhalter dem Pfad und dem Dateinamen des Blazor-Skripts. Den Speicherort des Skripts und den zu verwendenden Pfad finden Sie unter ASP.NET Core Blazor-Projektstruktur.

Wenn der Benutzer zurück zu einer App mit einer getrennten Verbindung navigiert, wird die erneute Verbindung sofort versucht, anstatt auf die Dauer des nächsten erneuten Verbindungsintervalls zu warten. Dieses Verhalten versucht, die Verbindung so schnell wie möglich für den Benutzer fortzusetzen.

Die Standardanzeigedauer für die erneute Verbindung verwendet eine berechnete Backoff-Strategie. Die ersten Wiederholungsversuche treten in schneller Folge auf, bevor berechnete Verzögerungen zwischen Versuchen eingeführt werden. Die Standardlogik zum Berechnen des Wiederholungsintervalls ist ein Implementierungsdetail, das ohne vorherige Benachrichtigung geändert werden kann, aber Sie finden die Standardlogik, die das Blazor Framework in der computeDefaultRetryInterval-Funktion verwendet (Referenzquelle).

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Passen Sie das Verhalten des Wiederholungsintervalls an, indem Sie eine Funktion zum Berechnen des Wiederholungsintervalls angeben. Im folgenden exponentiellen Backoff-Beispiel wird die Anzahl der vorherigen Wiederholungsversuche mit 1.000 ms multipliziert, um das Wiederholungsintervall zu berechnen. Wenn die Anzahl der vorherigen Versuche zum erneuten Verbinden (previousAttempts) größer als die maximale Wiederholungsgrenze (maxRetries) ist, wird null dem Wiederholungsintervall (retryIntervalMilliseconds) zugewiesen, um weitere Wiederholungsversuche zu beenden:

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

Eine Alternative besteht darin, die genaue Abfolge der Wiederholungsintervalle anzugeben. Nach dem letzten angegebenen Wiederholungsintervall enden die Wiederholungsversuche, da die retryIntervalMilliseconds-Funktion undefined zurückgibt:

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

Weitere Informationen zum Start von Blazor finden Sie unter Starten von ASP.NET Core Blazor.

Steuern, wann die Benutzeroberfläche für die erneute Verbindungsherstellung angezeigt wird

Die Steuerung, wann die Benutzeroberfläche für die erneute Verbindungsherstellung angezeigt wird, kann in den folgenden Situationen hilfreich sein:

  • Eine bereitgestellte App zeigt aufgrund von Timeouts beim Pingen, die durch interne Netzwerk- oder Internetlatenz verursacht werden, häufig die Benutzeroberfläche für die erneute Verbindungsherstellung an, und Sie möchten die Verzögerung erhöhen.
  • Eine App soll Benutzer früher darüber informieren, dass die Verbindung getrennt wurde, und Sie möchten die Verzögerung verkürzen.

Die Anzeigedauer der Benutzeroberfläche für die erneute Verbindungsherstellung wird durch Anpassen des Keep Alive-Intervalls und der Timeouts auf dem Client beeinflusst. Die Benutzeroberfläche für die erneute Verbindung wird angezeigt, wenn das Servertimeout auf dem Client erreicht ist (withServerTimeout, Abschnitt Clientkonfiguration). Das Ändern des Werts von withServerTimeout erfordert jedoch Änderungen an anderen Keep-Alive-, Timeout- und Handshake-Einstellungen, die in den folgenden Anleitungen beschrieben werden.

Allgemeine Empfehlungen für die Anweisungen:

  • Das Keep Alive-Intervall sollte zwischen Client- und Serverkonfigurationen übereinstimmen.
  • Timeouts sollte mindestens doppelt so groß sein wie der dem Keep Alive-Intervall zugewiesene Wert.

Serverkonfiguration

Legen Sie Folgendes fest:

  • ClientTimeoutInterval (Standardwert: 30 Sekunden): Die Zeitfensterclients müssen eine Nachricht senden, bevor der Server die Verbindung trennt.
  • HandshakeTimeout (Standardwert: 15 Sekunden): Das vom Server verwendete Intervall für das Timeout eingehender Handshakeanforderungen von Clients
  • KeepAliveInterval (Standardwert: 15 Sekunden): Das vom Server verwendete Intervall für das Senden von Keep-Alive-Pings an verbundene Clients. Beachten Sie, dass auch auf dem Client eine Einstellung für das Keep Alive-Intervall vorhanden ist, die mit dem Wert des Servers übereinstimmen sollte.

ClientTimeoutInterval und HandshakeTimeout können erhöht werden, und KeepAliveInterval kann gleich bleiben. Wichtig: Stellen Sie beim Ändern der Werte sicher, dass die Timeoutwerte mindestens doppelt so groß wie der Wert des Keep Alive-Intervalls ist und dass das Keep Alive-Intervall zwischen Server und Client übereinstimmt. Weitere Informationen finden Sie im Abschnitt Konfigurieren von SignalR-Timeouts und Keep-Alive im Client.

Im folgenden Beispiel:

  • ClientTimeoutInterval wird auf 60 Sekunden erhöht (Standardwert: 30 Sekunden).
  • HandshakeTimeout wird auf 30 Sekunden erhöht (Standardwert: 15 Sekunden).
  • KeepAliveInterval wird im Entwicklercode nicht festgelegt und verwendet den Standardwert von 15 Sekunden. Durch das Verringern des Werts des Keep Alive-Intervalls wird die Häufigkeit von Kommunikationspings erhöht. Dadurch erhöht sich auch die Last für die App, den Server und das Netzwerk. Achten Sie darauf, dass das Verringern des Keep Alive-Intervalls nicht zu Leistungseinbußen führt.

Blazor Web App (.NET 8 oder höher) in der Program-Datei des Serverprojekts:

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

Blazor Server in der Datei Program:

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

Weitere Informationen finden Sie im Abschnitt Handleroptionen für serverseitige Schaltkreise.

Clientkonfiguration

Legen Sie Folgendes fest:

  • withServerTimeout (Standardwert: 30 Sekunden): Konfiguriert das in Millisekunden angegebene Servertimeout für die Hubverbindung der Leitung.
  • withKeepAliveInterval (Standard: 15 Sekunden): Das in Millisekunden angegebene Intervall, in dem die Verbindung Keep Alive-Nachrichten sendet.

Das Servertimeout kann erhöht und das Keep Alive-Intervall unverändert bleiben. Wichtig: Stellen Sie beim Ändern der Werte sicher, dass das Servertimeout mindestens doppelt so groß wie der Wert des Keep Alive-Intervalls ist und dass die Keep Alive-Intervallwerte zwischen Server und Client übereinstimmen. Weitere Informationen finden Sie im Abschnitt Konfigurieren von SignalR-Timeouts und Keep-Alive im Client.

Im folgenden Beispiel für die Startkonfiguration (Speicherort des Blazor-Skripts) wird ein benutzerdefinierter Wert von 60 Sekunden für das Servertimeout verwendet. Das Keep Alive-Intervall (withKeepAliveInterval) wird nicht festgelegt und verwendet den Standardwert von 15 Sekunden.

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>

Wenn Sie eine Hubverbindung in einer Komponente erstellen, legen Sie das Servertimeout (WithServerTimeout, Standardwert 30 Sekunden) für HubConnectionBuilder fest. Legen Sie die HandshakeTimeout (Standardeinstellung 15 Sekunden) für die erstellte HubConnection fest. Stellen Sie sicher, dass die Timeouts mindestens doppelt so groß wie das Keep Alive-Intervall (WithKeepAliveInterval/KeepAliveInterval) sind und dass der Keep Alive-Wert zwischen Server und Client übereinstimmt.

Das folgende Beispiel basiert auf der Index-Komponente in SignalR mit dem Blazor-Tutorial. Das Servertimeout wird auf 60 Sekunden erhöht, das Handshaketimeout auf 30 Sekunden. Das Keep Alive-Intervall wird nicht festgelegt und verwendet den Standardwert von 15 Sekunden.

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

Legen Sie Folgendes fest:

  • serverTimeoutInMilliseconds (Standardwert: 30 Sekunden): Konfiguriert das in Millisekunden angegebene Servertimeout für die Hubverbindung der Leitung.
  • keepAliveIntervalInMilliseconds (Standard: 15 Sekunden): Das in Millisekunden angegebene Intervall, in dem die Verbindung Keep Alive-Nachrichten sendet.

Das Servertimeout kann erhöht und das Keep Alive-Intervall unverändert bleiben. Wichtig: Stellen Sie beim Ändern der Werte sicher, dass das Servertimeout mindestens doppelt so groß wie der Wert des Keep Alive-Intervalls ist und dass die Keep Alive-Intervallwerte zwischen Server und Client übereinstimmen. Weitere Informationen finden Sie im Abschnitt Konfigurieren von SignalR-Timeouts und Keep-Alive im Client.

Im folgenden Beispiel für die Startkonfiguration (Speicherort des Blazor-Skripts) wird ein benutzerdefinierter Wert von 60 Sekunden für das Servertimeout verwendet. Das Keep Alive-Intervall (keepAliveIntervalInMilliseconds) wird nicht festgelegt und verwendet den Standardwert von 15 Sekunden.

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

Wenn Sie eine Hubverbindung in einer Komponente erstellen, legen Sie ServerTimeout (Standardwert: 30 Sekunden) und HandshakeTimeout (Standardwert: 15 Sekunden) für die erstellte HubConnection fest. Stellen Sie sicher, dass die Timeouts mindestens doppelt so groß wie das Keep Alive-Intervall sind. Vergewissern Sie sich, dass das Keep Alive-Intervall zwischen Server und Client übereinstimmt.

Das folgende Beispiel basiert auf der Index-Komponente in SignalR mit dem Blazor-Tutorial. ServerTimeout wird auf 60 Sekunden und HandshakeTimeout auf 30 Sekunden erhöht. Das Keep Alive-Intervall (KeepAliveInterval) wird nicht festgelegt und verwendet den Standardwert von 15 Sekunden.

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

Trennen der Blazor-Verbindung mit dem Client

Ein Blazor-Kreislauf wird getrennt, wenn das unload-Seitenereignis ausgelöst wird. Um die Verbindung in anderen Szenarien auf dem Client zu trennen, rufen Sie Blazor.disconnect im entsprechenden Ereignishandler auf. Im folgenden Beispiel wird die Verbindung getrennt, wenn die Seite ausgeblendet wird (pagehide-Ereignis):

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

Weitere Informationen zum Start von Blazor finden Sie unter Starten von ASP.NET Core Blazor.

Serverseitige Verbindungshandler

Sie können einen Verbindungshandler definieren, mit dem Code bei Zustandsänderungen einer Benutzerverbindung ausgeführt werden kann. Ein Verbindungshandler wird durch das Ableiten von CircuitHandler und Registrieren der Klasse im Dienstcontainer der App implementiert. Im folgenden Beispiel für einen Verbindungshandlers werden geöffnete SignalR-Verbindungen nachverfolgt.

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

Verbindungshandler werden mithilfe von DI registriert. Bereichsbezogene Instanzen werden pro Verbindungsinstanz erstellt. Mithilfe von TrackingCircuitHandler aus dem Beispiel oben wird ein Singletondienst erstellt, weil der Zustand aller Verbindungen nachverfolgt werden muss.

Gehen Sie in der Program-Datei folgendermaßen vor:

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

In Startup.ConfigureServices von Startup.cs:

services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

Wenn die Methoden eines benutzerdefinierten Verbindungshandlers einen Ausnahmefehler auslösen, ist dieser Ausnahmefehler für die -Verbindung schwerwiegend. Umschließen Sie den Code in einer oder mehreren try-catch-Anweisungen mit Fehlerbehandlung und -protokollierung, um Ausnahmen im Code oder in aufgerufenen Methoden eines Handlers zu tolerieren.

Wenn eine Verbindung beendet wird, weil ein Benutzer die Verbindung getrennt hat und das Framework den Verbindungsstatus bereinigt, gibt das Framework den DI-Bereich der Verbindung frei. Wenn der Bereich freigegeben wird, werden alle Dienste im Verbindungsbereich verworfen, die System.IDisposable implementieren. Wenn ein DI-Dienst während der Freigabe einen Ausnahmefehler auslöst, protokolliert das Framework die Ausnahme. Weitere Informationen finden Sie unter Abhängigkeitsinjektion in ASP.NET Core Blazor.

Serverseitiger Verbindungshandler zum Erfassen von Benutzer*innen für benutzerdefinierte Dienste

Verwenden Sie einen CircuitHandler, um einen Benutzer aus dem AuthenticationStateProvider zu erfassen und den Benutzer in einem Dienst festzulegen. Weitere Informationen und Beispielcode finden Sie unter Zusätzliche serverseitige Sicherheitsszenarios für Blazor in ASP.NET Core.

Schließen von Verbindungen, wenn keine verbleibenden interaktiven Serverkomponenten vorhanden sind

Interaktive Serverkomponenten verarbeiten Ereignisse auf der Webbenutzeroberfläche mithilfe einer Echtzeitverbindung mit dem Browser. Diese wird als Verbindung bezeichnet. Eine Verbindung und der zugehörige Zustand werden erstellt, wenn eine interaktive Stammserverkomponente gerendert wird. Die Verbindung wird geschlossen, wenn keine interaktiven Serverkomponenten auf der Seite vorhanden sind, wodurch Serverressourcen freigegeben werden.

IHttpContextAccessor/HttpContext in Razor-Komponenten

IHttpContextAccessor muss mit interaktivem Rendering vermieden werden, da kein gültiger HttpContext verfügbar ist.

IHttpContextAccessor kann für Komponenten verwendet werden, die auf dem Server statisch gerendert werden. Es wird jedoch empfohlen, dies nach Möglichkeit zu vermeiden.

HttpContext kann nur in statisch gerenderten Stammkomponenten für allgemeine Aufgaben als kaskadierender Parameter verwendet werden, z. B. beim Überprüfen und Ändern von Headern oder anderen Eigenschaften in der App-Komponente (Components/App.razor). Der Wert lautet immer null zum interaktiven Rendern.

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

Für Szenarios, in denen HttpContext in interaktiven Komponenten erforderlich ist, empfehlen wir, die Daten über den permanenten Komponentenstatus vom Server zu übertragen. Weitere Informationen finden Sie unter Zusätzliche serverseitige Sicherheitsszenarien für Blazor in ASP.NET Core.

Verwenden Sie IHttpContextAccessor/HttpContext nicht direkt oder indirekt in den Razor-Komponenten serverseitiger Blazor-Apps. Blazor-Apps werden außerhalb des ASP.NET Core-Pipelinekontexts ausgeführt. Die Verfügbarkeit von HttpContext in IHttpContextAccessor kann nicht sichergestellt, und es ist auch nicht gewährleistet, dass HttpContext den Kontext zum Starten der Blazor-App enthält.

Der empfohlene Ansatz für das Übergeben des Anforderungsstatus an die Blazor-App erfolgt über Stammkomponentenparameter während des anfänglichen Renderings der App. Alternativ kann die App die Daten in einen bereichsbezogenen Dienst im Initialisierungslebenszyklusereignis der Stammkomponente kopieren, um sie in der gesamten App zu verwenden. Weitere Informationen finden Sie unter Zusätzliche serverseitige Sicherheitsszenarien für Blazor in ASP.NET Core.

Ein wichtiger Aspekt der serverseitigen Blazor-Sicherheit ist, dass Benutzer:innen, die an eine bestimmte Verbindung angefügt sind, möglicherweise irgendwann aktualisiert werden, nachdem die Blazor-Verbindung hergestellt wurde, IHttpContextAccessor aber nicht aktualisiert wird. Weitere Informationen zum Beheben dieses Problems mit benutzerdefinierten Diensten finden Sie unter Zusätzliche serverseitige Sicherheitsszenarien für ASP.NET Core Blazor.

Zusätzliche serverseitige Ressourcen