Condividi tramite


Linee guida di BlazorSignalR base per ASP.NET

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo illustra come configurare e gestire SignalR le connessioni nelle Blazor app.

Per indicazioni generali sulla configurazione di ASP.NET CoreSignalR, vedere gli argomenti nell'area Panoramica di ASP.NET Core SignalR della documentazione, in particolare ASP.NET configurazione coreSignalR.

Le app lato server usano ASP.NET Core SignalR per comunicare con il browser. SignalRLe condizioni di hosting e ridimensionamento si applicano alle app lato server.

Blazor funziona meglio quando si usano WebSocket come SignalR trasporto a causa di bassa latenza, affidabilità e sicurezza. Il polling lungo viene usato da SignalR quando WebSocket non è disponibile o quando l'app è configurata in modo esplicito per l'uso del polling lungo.

Servizio di Azure SignalR con riconnessione con stato

La riconnessione con stato (WithStatefulReconnect) è stata rilasciata con .NET 8, ma non è attualmente supportata per il servizio di Azure SignalR . Per altre informazioni, vedere Supporto della riconnessione con stato? (Azure/azure-signalr n. 1878)..

Compressione WebSocket per componenti Interactive Server

Per impostazione predefinita, i componenti di Interactive Server:

  • Abilitare la compressione per le connessioni WebSocket. DisableWebSocketCompression (impostazione predefinita: false) controlla la compressione WebSocket.

  • Adottare una frame-ancestors direttiva CSP (Content Security Policy) impostata su 'self', che consente solo l'incorporamento dell'app in un'origine <iframe> da cui viene servita l'app quando la compressione è abilitata o quando viene fornita una configurazione per il contesto WebSocket. ContentSecurityFrameAncestorPolicy controlla il provider di servizi di frame-ancestors configurazione.

Il frame-ancestors provider di servizi di configurazione può essere rimosso manualmente impostando il valore di ContentSecurityFrameAncestorsPolicy su null, in quanto è possibile configurare il provider di servizi di configurazione in modo centralizzato. Quando il frame-ancestors provider di servizi di configurazione viene gestito in modo centralizzato, è necessario prestare attenzione all'applicazione di un criterio ogni volta che viene eseguito il rendering del primo documento. Non è consigliabile rimuovere completamente i criteri, perché potrebbe rendere l'app vulnerabile agli attacchi.

Utilizzare ConfigureWebSocketAcceptContext per configurare per WebSocketAcceptContext le connessioni Websocket usate dai componenti del server. Per impostazione predefinita, viene applicato un criterio che abilita la compressione e imposta un provider di servizi di configurazione per i predecessori dei frame definiti in ContentSecurityFrameAncestorsPolicy .

Esempi di utilizzo:

Disabilitare la compressione impostando DisableWebSocketCompression su true, che riduce la vulnerabilità dell'app per l'attacco , ma può comportare una riduzione delle prestazioni:

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

Quando la compressione è abilitata, configurare un CSP più frame-ancestors rigoroso con un valore ( 'none' virgolette singole necessarie), che consente la compressione WebSocket, ma impedisce ai browser di incorporare l'app in qualsiasi <iframe>:

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

Quando la compressione è abilitata, rimuovere il frame-ancestors provider di servizi di configurazione impostando ContentSecurityFrameAncestorsPolicy su null. Questo scenario è consigliato solo per le app che impostano il provider di servizi di configurazione in modo centralizzato:

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

Importante

I browser applicano direttive CSP da più intestazioni CSP usando il valore di direttiva dei criteri più rigoroso. Pertanto, uno sviluppatore non può aggiungere un criterio più debole frame-ancestors che 'self' a scopo o per errore.

Le virgolette singole sono obbligatorie per il valore stringa passato a ContentSecurityFrameAncestorsPolicy:

Valori non supportati: none, self

'self'

Le opzioni aggiuntive includono la specifica di una o più origini host e di schemi.

Per le implicazioni relative alla sicurezza, vedere Linee guida per la mitigazione delle minacce per ASP.NET rendering lato server interattivo CoreBlazor. Per altre informazioni sulla frame-ancestors direttiva, vedere CSP: frame-ancestors (documentazione mdn).

Disabilitare la compressione delle risposte per Ricaricamento rapido

Quando si usa Ricaricamento rapido, disabilitare il middleware di compressione della risposta nell'ambienteDevelopment. Indipendentemente dal fatto che venga usato o meno il codice predefinito da un modello di progetto, chiamare UseResponseCompression sempre prima nella pipeline di elaborazione della richiesta.

Nel file Program:

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

Negoziazione tra origini lato SignalR client per l'autenticazione

Questa sezione illustra come configurare il client sottostante SignalRper l'invio di credenziali, ad esempio cookie o intestazioni di autenticazione HTTP.

Usare SetBrowserRequestCredentials per impostare Include le richieste tra le origini 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);
    }
}

Dove viene compilata una connessione hub, assegnare all'opzione HttpMessageHandler HttpMessageHandlerFactory :

private HubConnectionBuilder? hubConnection;

...

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

L'esempio precedente configura l'URL della connessione hub all'indirizzo URI assoluto in /chathub. L'URI può anche essere impostato tramite una stringa, ad esempio https://signalr.example.como tramite la configurazione. Navigation è un oggetto inserito NavigationManager.

Per altre informazioni, vedere configurazione di ASP.NET CoreSignalR.

Rendering lato client

Se la prerendering è configurata, la prerendering si verifica prima che venga stabilita la connessione client al server. Per altre informazioni, vedere Prerender ASP.NET Componenti di baseRazor.

Se la prerendering è configurata, la prerendering si verifica prima che venga stabilita la connessione client al server. Per altre informazioni, vedere gli articoli seguenti:

Dimensioni dello stato prerisorse e SignalR limite di dimensioni dei messaggi

Una dimensione dello stato prerendered di grandi dimensioni può superare Blazoril limite di dimensioni dei messaggi del SignalR circuito, il che comporta quanto segue:

  • L'inizializzazione del SignalR circuito non riesce con un errore nel client: Circuit host not initialized.
  • L'interfaccia utente di riconnessione nel client viene visualizzata quando il circuito ha esito negativo. Il ripristino non è possibile.

Per risolvere il problema, usare uno degli approcci seguenti:

  • Ridurre la quantità di dati inseriti nello stato pre-temporaneo.
  • Aumentare il limite di dimensioni del SignalR messaggio. AVVISO: l'aumento del limite può aumentare il rischio di attacchi Denial of Service (DoS).

Risorse aggiuntive sul lato client

Usare l'affinità di sessione (sessioni permanenti) per l'hosting webfarm sul lato server

Quando sono in uso più server back-end, l'app deve implementare l'affinità di sessione, detta anche sessioni permanenti. L'affinità di sessione garantisce che il circuito di un client si riconnette allo stesso server se la connessione viene interrotta, che è importante perché lo stato del client viene mantenuto solo nella memoria del server che ha stabilito il circuito del client.

L'errore seguente viene generato da un'app che non ha abilitato l'affinità di sessione in un webfarm:

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

Per altre informazioni sull'affinità di sessione con l'hosting del servizio app Azure, vedere Ospitare e distribuire ASP.NET app sul lato Blazor server core.

Servizio di Azure SignalR

Il servizio di Azure SignalR facoltativo funziona insieme all'hub dell'app SignalR per aumentare le prestazioni di un'app lato server a un numero elevato di connessioni simultanee. Inoltre, la copertura globale del servizio e i data center ad alte prestazioni aiutano significativamente a ridurre la latenza a causa della geografia.

Il servizio non è necessario per Blazor le app ospitate nel servizio app Azure o nelle app di Azure Container, ma può essere utile in altri ambienti di hosting:

  • Per facilitare la scalabilità orizzontale delle connessioni.
  • Gestire la distribuzione globale.

Per altre informazioni, vedere Ospitare e distribuire ASP.NET app sul lato Blazor server core.

Opzioni del gestore del circuito lato server

Configurare il circuito con CircuitOptions. Visualizzare i valori predefiniti nell'origine di riferimento.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Leggere o impostare le opzioni nel Program file con un delegato di opzioni su AddInteractiveServerComponents. Il {OPTION} segnaposto rappresenta l'opzione e il {VALUE} segnaposto è il valore .

Nel file Program:

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

Leggere o impostare le opzioni nel Program file con un delegato di opzioni su AddServerSideBlazor. Il {OPTION} segnaposto rappresenta l'opzione e il {VALUE} segnaposto è il valore .

Nel file Program:

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

Leggere o impostare le opzioni in Startup.ConfigureServices con un delegato di opzioni su AddServerSideBlazor. Il {OPTION} segnaposto rappresenta l'opzione e il {VALUE} segnaposto è il valore .

In Startup.ConfigureServices di Startup.cs:

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

Per configurare HubConnectionContext, usare HubConnectionContextOptions con AddHubOptions. Visualizzare le impostazioni predefinite per le opzioni del contesto di connessione hub nell'origine di riferimento. Per le descrizioni delle opzioni nella SignalR documentazione, vedere configurazione di ASP.NET CoreSignalR. Il {OPTION} segnaposto rappresenta l'opzione e il {VALUE} segnaposto è il valore .

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Nel file Program:

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

Nel file Program:

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

In Startup.ConfigureServices di Startup.cs:

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

Avviso

Il valore predefinito di MaximumReceiveMessageSize è 32 KB. L'aumento del valore può aumentare il rischio di attacchi Denial of Service (DoS).

Blazor si basa su impostato su MaximumParallelInvocationsPerClient 1, ovvero il valore predefinito. Per altre informazioni, vedere MaximumParallelInvocationsPerClient > 1 interrompe il caricamento dei file in Blazor Server modalità (dotnet/aspnetcore #53951).

Per informazioni sulla gestione della memoria, vedere Ospitare e distribuire ASP.NET app sul lato Blazor server core.

Blazor opzioni hub

Configurare MapBlazorHub le opzioni per controllare HttpConnectionDispatcherOptions l'hub Blazor . Visualizzare le impostazioni predefinite per le opzioni del dispatcher della connessione hub nell'origine di riferimento. Il {OPTION} segnaposto rappresenta l'opzione e il {VALUE} segnaposto è il valore .

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Inserire la chiamata a dopo la chiamata a app.MapBlazorHub app.MapRazorComponents nel file dell'app Program :

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

La configurazione dell'hub usato da AddInteractiveServerRenderMode con ha esito negativo con MapBlazorHub :AmbiguousMatchException

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

Per risolvere il problema per le app destinate a .NET 8, assegnare la precedenza più alta all'hub configurato Blazor personalizzato usando il WithOrder metodo :

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

Per ulteriori informazioni, vedi le seguenti risorse:

Fornire le opzioni a app.MapBlazorHub nel file dell'app Program :

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

Fornire le opzioni a app.MapBlazorHub nella configurazione del routing degli endpoint:

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

Dimensione massima dei messaggi di ricezione

Questa sezione si applica solo ai progetti che implementano SignalR.

Le dimensioni massime dei messaggi in arrivo SignalR consentite per i HubOptions.MaximumReceiveMessageSize metodi hub sono limitate da (impostazione predefinita: 32 KB). SignalR messaggi più grandi di MaximumReceiveMessageSize generano un errore. Il framework non impone un limite alle dimensioni di un SignalR messaggio dall'hub a un client.

Quando SignalR la registrazione non è impostata su Debug o Traccia, nella console degli strumenti di sviluppo del browser viene visualizzato un errore relativo alle dimensioni del messaggio:

Errore: connessione disconnessa con errore 'Errore: il server ha restituito un errore alla chiusura: Connessione chiusa con un errore'.

Quando SignalR la registrazione lato server è impostata su Debug o Traccia, la registrazione lato server visualizza un errore InvalidDataException relativo alle dimensioni del messaggio.

appsettings.Development.json:

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

Errore:

System.IO.InvalidDataException: è stata superata la dimensione massima del messaggio di 32768B. Le dimensioni del messaggio possono essere configurate in AddHubOptions.

Un approccio prevede l'aumento del limite impostando MaximumReceiveMessageSize nel Program file. L'esempio seguente imposta la dimensione massima del messaggio di ricezione su 64 KB:

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

L'aumento del SignalR limite di dimensioni dei messaggi in arrivo comporta il costo di richiedere più risorse server e aumenta il rischio di attacchi Denial of Service (DoS). Inoltre, la lettura di una grande quantità di contenuto in memoria come stringhe o matrici di byte può anche comportare allocazioni che funzionano male con il Garbage Collector, causando ulteriori penali per le prestazioni.

Un'opzione migliore per la lettura di payload di grandi dimensioni consiste nell'inviare il contenuto in blocchi più piccoli ed elaborare il payload come .Stream Questa operazione può essere usata durante la lettura di payload JSON di interoperabilità JavaScript (JS) di grandi dimensioni o se JS i dati di interoperabilità sono disponibili come byte non elaborati. Per un esempio che illustra l'invio di payload binari di grandi dimensioni nelle app lato server che usano tecniche simili al componente, vedere l'app di esempio Di invio binario e l'esempioInputLargeTextAreaBlazor di componente.InputFile

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

I moduli che elaborano payload di grandi dimensioni possono SignalR anche usare direttamente l'interoperabilità di streaming JS . Per altre informazioni, vedere Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor. Per un esempio di moduli che trasmette <textarea> i dati al server, vedere Risolvere i problemi relativi ai moduli ASP.NET CoreBlazor.

Un approccio prevede l'aumento del limite impostando MaximumReceiveMessageSize nel Program file. L'esempio seguente imposta la dimensione massima del messaggio di ricezione su 64 KB:

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

L'aumento del SignalR limite di dimensioni dei messaggi in arrivo comporta il costo di richiedere più risorse server e aumenta il rischio di attacchi Denial of Service (DoS). Inoltre, la lettura di una grande quantità di contenuto in memoria come stringhe o matrici di byte può anche comportare allocazioni che funzionano male con il Garbage Collector, causando ulteriori penali per le prestazioni.

Un'opzione migliore per la lettura di payload di grandi dimensioni consiste nell'inviare il contenuto in blocchi più piccoli ed elaborare il payload come .Stream Questa operazione può essere usata durante la lettura di payload JSON di interoperabilità JavaScript (JS) di grandi dimensioni o se JS i dati di interoperabilità sono disponibili come byte non elaborati. Per un esempio che illustra l'invio di payload binari di grandi dimensioni in Blazor Server che usano tecniche simili al InputFile componente, vedi l'app di esempio Di invio binario e l'esempioInputLargeTextArea Blazordi componente.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

I moduli che elaborano payload di grandi dimensioni possono SignalR anche usare direttamente l'interoperabilità di streaming JS . Per altre informazioni, vedere Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor. Per un esempio di moduli che trasmette i <textarea> dati in un'appBlazor Server, vedere Risolvere i problemi relativi ai moduli principali di ASP.NETBlazor.

Aumentare il limite impostando MaximumReceiveMessageSize in Startup.ConfigureServices:

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

L'aumento del SignalR limite di dimensioni dei messaggi in arrivo comporta il costo di richiedere più risorse server e aumenta il rischio di attacchi Denial of Service (DoS). Inoltre, la lettura di una grande quantità di contenuto in memoria come stringhe o matrici di byte può anche comportare allocazioni che funzionano male con il Garbage Collector, causando ulteriori penali per le prestazioni.

Quando si sviluppa codice che trasferisce una grande quantità di dati, tenere presente quanto segue:

  • Sfruttare il supporto dell'interoperabilità di streaming JS nativo per trasferire dati superiori al limite di dimensioni dei SignalR messaggi in ingresso:
  • Suggerimenti generali:
    • Non allocare oggetti di grandi dimensioni nel JS codice C#.
    • Liberare memoria utilizzata al termine o all'annullamento del processo.
    • Applicare i requisiti aggiuntivi seguenti per scopi di sicurezza:
      • Dichiarare la dimensione massima del file o dei dati che è possibile passare.
      • Dichiarare la frequenza di caricamento minima dal client al server.
    • Dopo che i dati sono stati ricevuti dal server, i dati possono essere:
      • Archiviato temporaneamente in un buffer di memoria fino a quando non vengono raccolti tutti i segmenti.
      • Consumata immediatamente. Ad esempio, i dati possono essere archiviati immediatamente in un database o scritti su disco quando ogni segmento viene ricevuto.
  • Suddividere i dati in parti più piccole e inviare i segmenti di dati in sequenza fino a quando non vengono ricevuti tutti i dati dal server.
  • Non allocare oggetti di grandi dimensioni nel JS codice C#.
  • Non bloccare il thread principale dell'interfaccia utente per lunghi periodi durante l'invio o la ricezione di dati.
  • Liberare memoria utilizzata al termine o all'annullamento del processo.
  • Applicare i requisiti aggiuntivi seguenti per scopi di sicurezza:
    • Dichiarare la dimensione massima del file o dei dati che è possibile passare.
    • Dichiarare la frequenza di caricamento minima dal client al server.
  • Dopo che i dati sono stati ricevuti dal server, i dati possono essere:
    • Archiviato temporaneamente in un buffer di memoria fino a quando non vengono raccolti tutti i segmenti.
    • Consumata immediatamente. Ad esempio, i dati possono essere archiviati immediatamente in un database o scritti su disco quando ogni segmento viene ricevuto.

Blazor Configurazione della route dell'endpoint dell'hub sul lato server

Program Nel file chiamare MapBlazorHub per eseguire il mapping dell'oggetto BlazorHub al percorso predefinito dell'app. Lo Blazor script (blazor.*.js) punta automaticamente all'endpoint creato da MapBlazorHub.

Riflettere lo stato della connessione lato server nell'interfaccia utente

Se il client rileva una connessione persa al server, viene visualizzata un'interfaccia utente predefinita all'utente mentre il client tenta di riconnettersi:

Interfaccia utente di riconnessione predefinita.

Interfaccia utente di riconnessione predefinita.

Se la riconnessione non riesce, all'utente viene richiesto di riprovare o ricaricare la pagina:

Interfaccia utente di ripetizione dei tentativi predefinita.

Interfaccia utente di ripetizione dei tentativi predefinita.

Se la riconnessione ha esito positivo, lo stato utente viene spesso perso. È possibile aggiungere codice personalizzato a qualsiasi componente per salvare e ricaricare lo stato utente in caso di errori di connessione. Per altre informazioni, vedere ASP.NET Gestione dello stato coreBlazor.

Per personalizzare l'interfaccia utente, definire un singolo elemento con un id di components-reconnect-modal nel contenuto dell'elemento <body> . Nell'esempio seguente l'elemento viene inserita nel App componente .

App.razor:

Per personalizzare l'interfaccia utente, definire un singolo elemento con un id di components-reconnect-modal nel contenuto dell'elemento <body> . Nell'esempio seguente l'elemento viene inserita nella pagina host.

Pages/_Host.cshtml:

Per personalizzare l'interfaccia utente, definire un singolo elemento con un id di components-reconnect-modal nel contenuto dell'elemento <body> . Nell'esempio seguente l'elemento viene inserita nella pagina di layout.

Pages/_Layout.cshtml:

Per personalizzare l'interfaccia utente, definire un singolo elemento con un id di components-reconnect-modal nel contenuto dell'elemento <body> . Nell'esempio seguente l'elemento viene inserita nella pagina host.

Pages/_Host.cshtml:

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

Nota

Se viene eseguito il rendering di più elementi con un id oggetto dell'app components-reconnect-modal , solo il primo elemento di cui è stato eseguito il rendering riceve le modifiche della classe CSS per visualizzare o nascondere l'elemento.

Aggiungere gli stili CSS seguenti al foglio di stile del sito.

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

Nella tabella seguente vengono descritte le classi CSS applicate all'elemento components-reconnect-modal dal Blazor framework.

Classe CSS Indica...
components-reconnect-show Connessione persa. Il client sta tentando di riconnettersi. Mostra il modale.
components-reconnect-hide Viene ristabilita una connessione attiva al server. Nascondere il modale.
components-reconnect-failed La riconnessione non è riuscita, probabilmente a causa di un errore di rete. Per tentare la riconnessione, chiamare window.Blazor.reconnect() in JavaScript.
components-reconnect-rejected Riconnessione rifiutata. Il server è stato raggiunto ma ha rifiutato la connessione e lo stato dell'utente nel server viene perso. Per ricaricare l'app, chiamare location.reload() in JavaScript. Questo stato di connessione può risultare quando:
  • Si verifica un arresto anomalo del circuito sul lato server.
  • Il client è disconnesso abbastanza a lungo per consentire al server di eliminare lo stato dell'utente. Le istanze dei componenti dell'utente vengono eliminate.
  • Il server viene riavviato o il processo di lavoro dell'app viene riciclato.

Personalizzare il ritardo prima che venga visualizzata l'interfaccia utente di riconnessione impostando la transition-delay proprietà nel css del sito per l'elemento modale. L'esempio seguente imposta il ritardo di transizione da 500 ms (impostazione predefinita) a 1.000 ms (1 secondo).

wwwroot/app.css:

wwwroot/css/site.css:

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

Per visualizzare il tentativo di riconnessione corrente, definire un elemento con un id di components-reconnect-current-attempt. Per visualizzare il numero massimo di tentativi di riconnessione, definire un elemento con un id di components-reconnect-max-retries. L'esempio seguente inserisce questi elementi all'interno di un elemento modale tentativo di riconnessione dopo l'esempio precedente.

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

Quando viene visualizzata la modale di riconnessione personalizzata, esegue il rendering del contenuto seguente con un contatore di tentativi di riconnessione:

C'è stato un problema con la connessione! (Tentativo di riconnessione corrente: 1/ 8)

Rendering lato server

Per impostazione predefinita, i componenti vengono prerisorsi nel server prima che venga stabilita la connessione client al server. Per altre informazioni, vedere Prerender ASP.NET Componenti di baseRazor.

Per impostazione predefinita, i componenti vengono prerisorsi nel server prima che venga stabilita la connessione client al server. Per altre informazioni, vedere Component Tag Helper in ASP.NET Core.

Monitorare l'attività del circuito lato server

Monitorare l'attività del circuito in ingresso usando il CreateInboundActivityHandler metodo su CircuitHandler. L'attività del circuito in ingresso è qualsiasi attività inviata dal browser al server, ad esempio eventi dell'interfaccia utente o JavaScript-to-.NET chiamate di interoperabilità.

Ad esempio, è possibile usare un gestore di attività del circuito per rilevare se il client è inattiva e registrarne l'ID circuito (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;
    }
}

Registrare il servizio nel Program file. Nell'esempio seguente viene configurato il timeout di inattività predefinito da cinque minuti a cinque secondi per testare l'implementazione precedente IdleCircuitHandler :

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

I gestori di attività del circuito forniscono anche un approccio per l'accesso ai servizi con Blazor ambito da altriBlazor ambiti di inserimento delle dipendenze (DI). Per altre informazioni ed esempi, vedere:

Blazor avvio

Configurare l'avvio manuale del BlazorSignalR circuito nel App.razor file di un Blazor Web Appoggetto :

Configurare l'avvio manuale del BlazorSignalR circuito nel Pages/_Host.cshtml file (Blazor Server):

Configurare l'avvio manuale del BlazorSignalR circuito nel Pages/_Layout.cshtml file (Blazor Server):

Configurare l'avvio manuale del BlazorSignalR circuito nel Pages/_Host.cshtml file (Blazor Server):

  • Aggiungere un autostart="false" attributo al <script> tag per lo blazor.*.js script.
  • Inserire uno script che chiama Blazor.start() dopo il caricamento dello Blazor script e all'interno del tag di chiusura </body> .

Quando autostart è disabilitato, qualsiasi aspetto dell'app che non dipende dal circuito funziona normalmente. Ad esempio, il routing lato client è operativo. Tuttavia, qualsiasi aspetto che dipende dal circuito non è operativo fino a quando Blazor.start() non viene chiamato. Il comportamento dell'app è imprevedibile senza un circuito stabilito. Ad esempio, i metodi dei componenti non vengono eseguiti mentre il circuito è disconnesso.

Per altre informazioni, tra cui come inizializzare Blazor quando il documento è pronto e come concatenare a un JS Promiseoggetto , vedere ASP.NET avvio di CoreBlazor.

Configurare SignalR timeout e Keep-Alive nel client

Configurare i valori seguenti per il client:

  • withServerTimeout: configura il timeout del server in millisecondi. Se questo timeout è trascorso senza ricevere messaggi dal server, la connessione viene terminata con un errore. Il valore di timeout predefinito è di 30 secondi. Il timeout del server deve essere almeno doppio del valore assegnato all'intervallo Keep-Alive (withKeepAliveInterval).
  • withKeepAliveInterval: configura l'intervallo Keep-Alive in millisecondi (intervallo predefinito in cui eseguire il ping del server). Questa impostazione consente al server di rilevare disconnessioni disconnesse, ad esempio quando un client scollega il computer dalla rete. Il ping viene eseguito al massimo con la frequenza con cui viene eseguito il ping del server. Se il server esegue il ping ogni cinque secondi, assegnando un valore inferiore a 5000 (5 secondi) esegue il ping ogni cinque secondi. Il valore predefinito è 15 secondi. L'intervallo Keep-Alive deve essere minore o uguale alla metà del valore assegnato al timeout del server (withServerTimeout).

L'esempio seguente per il App.razor file (Blazor Web App) mostra l'assegnazione dei valori predefiniti.

Blazor Web App:

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

L'esempio seguente per il Pages/_Host.cshtml file (Blazor Server, tutte le versioni tranne ASP.NET Core in .NET 6) o Pages/_Layout.cshtml il file (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>

Nell'esempio precedente il {BLAZOR SCRIPT} segnaposto è il percorso dello script e il Blazor nome del file. Per la posizione dello script e il percorso da usare, vedere ASP.NET struttura del progetto CoreBlazor.

Quando si crea una connessione hub in un componente, impostare ServerTimeout (impostazione predefinita: 30 secondi) e KeepAliveInterval (impostazione predefinita: 15 secondi) in HubConnectionBuilder. Impostare ( HandshakeTimeout impostazione predefinita: 15 secondi) nell'oggetto compilato HubConnection. L'esempio seguente illustra l'assegnazione di valori predefiniti:

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

Configurare i valori seguenti per il client:

  • serverTimeoutInMilliseconds: timeout del server in millisecondi. Se questo timeout è trascorso senza ricevere messaggi dal server, la connessione viene terminata con un errore. Il valore di timeout predefinito è di 30 secondi. Il timeout del server deve essere almeno doppio del valore assegnato all'intervallo Keep-Alive (keepAliveIntervalInMilliseconds).
  • keepAliveIntervalInMilliseconds: intervallo predefinito in cui effettuare il ping del server. Questa impostazione consente al server di rilevare disconnessioni disconnesse, ad esempio quando un client scollega il computer dalla rete. Il ping viene eseguito al massimo con la frequenza con cui viene eseguito il ping del server. Se il server esegue il ping ogni cinque secondi, assegnando un valore inferiore a 5000 (5 secondi) esegue il ping ogni cinque secondi. Il valore predefinito è 15 secondi. L'intervallo Keep-Alive deve essere minore o uguale alla metà del valore assegnato al timeout del server (serverTimeoutInMilliseconds).

L'esempio seguente per il Pages/_Host.cshtml file (Blazor Server, tutte le versioni tranne ASP.NET Core in .NET 6) o Pages/_Layout.cshtml il file (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>

Nell'esempio precedente il {BLAZOR SCRIPT} segnaposto è il percorso dello script e il Blazor nome del file. Per la posizione dello script e il percorso da usare, vedere ASP.NET struttura del progetto CoreBlazor.

Quando si crea una connessione hub in un componente, impostare ServerTimeout (impostazione predefinita: 30 secondi), HandshakeTimeout (impostazione predefinita: 15 secondi) e KeepAliveInterval (impostazione predefinita: 15 secondi) nell'oggetto compilato HubConnection. L'esempio seguente illustra l'assegnazione di valori predefiniti:

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

Quando si modificano i valori del timeout del server (ServerTimeout) o l'intervallo Keep-Alive (KeepAliveInterval):

  • Il timeout del server deve essere pari almeno al doppio del valore assegnato all'intervallo Keep-Alive.
  • L'intervallo Keep-Alive deve essere minore o uguale alla metà del valore assegnato al timeout del server.

Per altre informazioni, vedere le sezioni Distribuzione globale e errori di connessione degli articoli seguenti:

Modificare il gestore di riconnessione lato server

Gli eventi di connessione del gestore di riconnessione possono essere modificati per comportamenti personalizzati, ad esempio:

  • Per notificare all'utente se la connessione viene eliminata.
  • Per eseguire la registrazione (dal client) quando un circuito è connesso.

Per modificare gli eventi di connessione, registrare i callback per le modifiche di connessione seguenti:

  • Le connessioni eliminate usano onConnectionDown.
  • Le connessioni stabilite/ristabilite usano onConnectionUp.

Sia onConnectionDown che onConnectionUp devono essere specificati.

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>

Nell'esempio precedente il {BLAZOR SCRIPT} segnaposto è il percorso dello script e il Blazor nome del file. Per la posizione dello script e il percorso da usare, vedere ASP.NET struttura del progetto CoreBlazor.

Aggiornare automaticamente la pagina quando la riconnessione lato server non riesce

Il comportamento di riconnessione predefinito richiede all'utente di eseguire un'azione manuale per aggiornare la pagina dopo che la riconnessione non riesce. Tuttavia, è possibile usare un gestore di riconnessione personalizzato per aggiornare automaticamente la pagina:

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>

Nell'esempio precedente il {BLAZOR SCRIPT} segnaposto è il percorso dello script e il Blazor nome del file. Per la posizione dello script e il percorso da usare, vedere ASP.NET struttura del progetto CoreBlazor.

Creare il file seguente wwwroot/boot.js .

Blazor Web App:

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

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

    let isCanceled = false;

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

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

        if (isCanceled) {
          return;
        }

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

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

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

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

  let currentReconnectionProcess = null;

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

Blazor Server:

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

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

    let isCanceled = false;

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

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

        if (isCanceled) {
          return;
        }

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

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

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

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

  let currentReconnectionProcess = null;

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

Per altre informazioni sull'avvio di Blazor, vedere Avvio di ASP.NET Core Blazor.

Modificare il numero di tentativi e l'intervallo di riconnessione sul lato server

Per modificare il numero di tentativi di riconnessione e l'intervallo, impostare il numero di tentativi (maxRetries) e il periodo in millisecondi consentiti per ogni tentativo di ripetizione (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>

Nell'esempio precedente il {BLAZOR SCRIPT} segnaposto è il percorso dello script e il Blazor nome del file. Per la posizione dello script e il percorso da usare, vedere ASP.NET struttura del progetto CoreBlazor.

Quando l'utente torna a un'app con un circuito disconnesso, la riconnessione viene tentata immediatamente anziché attendere la durata dell'intervallo di riconnessione successivo. Questo comportamento cerca di riprendere la connessione il più rapidamente possibile per l'utente.

L'intervallo di riconnessione predefinito usa una strategia di backoff calcolata. I primi tentativi di riconnessione si verificano in rapida successione prima che vengano introdotti ritardi calcolati tra i tentativi. La logica predefinita per il calcolo dell'intervallo di ripetizione dei tentativi è un dettaglio di implementazione soggetto a modifiche senza preavviso, ma è possibile trovare la logica predefinita usata dal Blazor framework nella computeDefaultRetryInterval funzione (origine di riferimento).

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Personalizzare il comportamento dell'intervallo di ripetizione dei tentativi specificando una funzione per calcolare l'intervallo di ripetizione dei tentativi. Nell'esempio di backoff esponenziale seguente il numero di tentativi di riconnessione precedenti viene moltiplicato per 1.000 ms per calcolare l'intervallo di ripetizione dei tentativi. Quando il numero di tentativi precedenti di riconnessione (previousAttempts) è maggiore del limite massimo di tentativi (maxRetries), null viene assegnato all'intervallo tra tentativi (retryIntervalMilliseconds) per interrompere ulteriori tentativi di riconnessione:

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

Un'alternativa consiste nel specificare la sequenza esatta di intervalli di ripetizione dei tentativi. Dopo l'ultimo intervallo di ripetizione dei tentativi specificato, i nuovi tentativi vengono interrotti perché la retryIntervalMilliseconds funzione restituisce undefined:

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

Per altre informazioni sull'avvio di Blazor, vedere Avvio di ASP.NET Core Blazor.

Controllare quando viene visualizzata l'interfaccia utente di riconnessione

Il controllo quando viene visualizzata l'interfaccia utente di riconnessione può essere utile nelle situazioni seguenti:

  • Un'app distribuita visualizza spesso l'interfaccia utente di riconnessione a causa di timeout del ping causati dalla rete interna o dalla latenza Internet e si vuole aumentare il ritardo.
  • Un'app deve segnalare agli utenti che la connessione è stata interrotta prima e si vuole ridurre il ritardo.

La tempistica dell'aspetto dell'interfaccia utente di riconnessione è influenzata modificando l'intervallo Keep-Alive e i timeout nel client. L'interfaccia utente di riconnessione viene visualizzata quando viene raggiunto il timeout del server nel client (withServerTimeoutsezione Configurazione client). Tuttavia, la modifica del valore di withServerTimeout richiede modifiche ad altre impostazioni Keep-Alive, timeout e handshake descritte nelle indicazioni seguenti.

Come raccomandazioni generali per le indicazioni seguenti:

  • L'intervallo Keep-Alive deve corrispondere tra le configurazioni client e server.
  • I timeout devono essere almeno il doppio del valore assegnato all'intervallo Keep-Alive.

Configurazione del server

Impostare i seguenti elementi:

  • ClientTimeoutInterval (impostazione predefinita: 30 secondi): i client dell'intervallo di tempo devono inviare un messaggio prima che il server chiuda la connessione.
  • HandshakeTimeout (impostazione predefinita: 15 secondi): intervallo usato dal server per timeout delle richieste di handshake in ingresso da parte dei client.
  • KeepAliveInterval (impostazione predefinita: 15 secondi): intervallo usato dal server per inviare ping keep-alive ai client connessi. Si noti che è disponibile anche un'impostazione di intervallo Keep-Alive nel client, che deve corrispondere al valore del server.

E ClientTimeoutInterval HandshakeTimeout può essere aumentato e può KeepAliveInterval rimanere lo stesso. La considerazione importante è che, se si modificano i valori, assicurarsi che i timeout siano almeno il doppio del valore dell'intervallo Keep-Alive e che l'intervallo Keep-Alive corrisponda tra server e client. Per altre informazioni, vedere la sezione Configurare SignalR i timeout e Keep-Alive nel client .

Nell'esempio seguente :

  • Viene ClientTimeoutInterval aumentato a 60 secondi (valore predefinito: 30 secondi).
  • Viene HandshakeTimeout aumentato a 30 secondi (valore predefinito: 15 secondi).
  • Non KeepAliveInterval è impostato nel codice per sviluppatori e usa il valore predefinito di 15 secondi. La riduzione del valore dell'intervallo Keep-Alive aumenta la frequenza dei ping di comunicazione, aumentando così il carico sull'app, sul server e sulla rete. È necessario prestare attenzione per evitare di introdurre prestazioni scarse quando si riduce l'intervallo Keep-Alive.

Blazor Web App (.NET 8 o versione successiva) nel file del Program progetto server:

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

Blazor ServerProgram nel file:

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

Per altre informazioni, vedere la sezione Opzioni del gestore del circuito lato server.

Configurazione del client

Impostare i seguenti elementi:

  • withServerTimeout (impostazione predefinita: 30 secondi): configura il timeout del server, specificato in millisecondi, per la connessione dell'hub del circuito.
  • withKeepAliveInterval (impostazione predefinita: 15 secondi): intervallo, specificato in millisecondi, in cui la connessione invia messaggi Keep-Alive.

Il timeout del server può essere aumentato e l'intervallo Keep-Alive può rimanere invariato. La considerazione importante è che se si modificano i valori, assicurarsi che il timeout del server sia almeno doppio del valore dell'intervallo Keep-Alive e che i valori dell'intervallo Keep-Alive corrispondano tra server e client. Per altre informazioni, vedere la sezione Configurare SignalR i timeout e Keep-Alive nel client .

Nell'esempio di configurazione di avvio seguente (posizione dello Blazor script), viene usato un valore personalizzato di 60 secondi per il timeout del server. L'intervallo Keep-Alive (withKeepAliveInterval) non è impostato e usa il valore predefinito di 15 secondi.

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>

Quando si crea una connessione hub in un componente, impostare il timeout del server (WithServerTimeoutimpostazione predefinita: 30 secondi) in HubConnectionBuilder. Impostare ( HandshakeTimeout impostazione predefinita: 15 secondi) nell'oggetto compilato HubConnection. Verificare che i timeout siano almeno il doppio dell'intervallo Keep-Alive (WithKeepAliveInterval/KeepAliveInterval) e che il valore Keep-Alive corrisponda tra server e client.

L'esempio seguente si basa sul Index componente nell'esercitazioneSignalR con .Blazor Il timeout del server viene aumentato a 60 secondi e il timeout dell'handshake viene aumentato a 30 secondi. L'intervallo Keep-Alive non è impostato e usa il valore predefinito di 15 secondi.

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

Impostare i seguenti elementi:

  • serverTimeoutInMilliseconds (impostazione predefinita: 30 secondi): configura il timeout del server, specificato in millisecondi, per la connessione dell'hub del circuito.
  • keepAliveIntervalInMilliseconds (impostazione predefinita: 15 secondi): intervallo, specificato in millisecondi, in cui la connessione invia messaggi Keep-Alive.

Il timeout del server può essere aumentato e l'intervallo Keep-Alive può rimanere invariato. La considerazione importante è che se si modificano i valori, assicurarsi che il timeout del server sia almeno doppio del valore dell'intervallo Keep-Alive e che i valori dell'intervallo Keep-Alive corrispondano tra server e client. Per altre informazioni, vedere la sezione Configurare SignalR i timeout e Keep-Alive nel client .

Nell'esempio di configurazione di avvio seguente (posizione dello Blazor script), viene usato un valore personalizzato di 60 secondi per il timeout del server. L'intervallo Keep-Alive (keepAliveIntervalInMilliseconds) non è impostato e usa il valore predefinito di 15 secondi.

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>

Quando si crea una connessione hub in un componente, impostare ( ServerTimeout impostazione predefinita: 30 secondi) e HandshakeTimeout (impostazione predefinita: 15 secondi) nell'oggetto compilato HubConnection. Verificare che i timeout siano almeno il doppio dell'intervallo Keep-Alive. Verificare che l'intervallo Keep-Alive corrisponda tra server e client.

L'esempio seguente si basa sul Index componente nell'esercitazioneSignalR con .Blazor L'oggetto ServerTimeout viene aumentato a 60 secondi e viene HandshakeTimeout aumentato a 30 secondi. L'intervallo Keep-Alive (KeepAliveInterval) non è impostato e usa il valore predefinito di 15 secondi.

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

Disconnettere BlazorSignalR il circuito dal client

BlazorIl circuito viene SignalR disconnesso quando viene attivato l'evento unload della pagina. Per disconnettere il circuito per altri scenari nel client, richiamare Blazor.disconnect nel gestore eventi appropriato. Nell'esempio seguente il circuito viene disconnesso quando la pagina è nascosta (pagehide evento):

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

Per altre informazioni sull'avvio di Blazor, vedere Avvio di ASP.NET Core Blazor.

Gestore del circuito lato server

È possibile definire un gestore del circuito, che consente l'esecuzione del codice in caso di modifiche allo stato del circuito di un utente. Un gestore del circuito viene implementato derivando e CircuitHandler registrando la classe nel contenitore del servizio dell'app. L'esempio seguente di un gestore del circuito tiene traccia delle connessioni aperte SignalR .

TrackingCircuitHandler.cs:

using Microsoft.AspNetCore.Components.Server.Circuits;

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

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

        return Task.CompletedTask;
    }

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

        return Task.CompletedTask;
    }

    public int ConnectedCircuits => circuits.Count;
}

I gestori di circuito vengono registrati tramite l'inserimento delle dipendenze. Le istanze con ambito vengono create per ogni istanza di un circuito. Usando nell'esempio TrackingCircuitHandler precedente, viene creato un servizio singleton perché è necessario tenere traccia dello stato di tutti i circuiti.

Nel file Program:

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

In Startup.ConfigureServices di Startup.cs:

services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();

Se i metodi di un gestore del circuito personalizzato generano un'eccezione non gestita, l'eccezione è irreversibile per il circuito. Per tollerare le eccezioni nel codice o nei metodi chiamati di un gestore, eseguire il wrapping del codice in una o più try-catch istruzioni con la gestione e la registrazione degli errori.

Quando un circuito termina perché un utente è disconnesso e il framework pulisce lo stato del circuito, il framework elimina l'ambito di inserimento delle dipendenze del circuito. L'eliminazione dell'ambito elimina tutti i servizi di inserimento con ambito circuito che implementano System.IDisposable. Se un servizio di inserimento delle dipendenze genera un'eccezione non gestita durante l'eliminazione, il framework registra l'eccezione. Per altre informazioni, vedere ASP.NET Core Blazor dependency injection.

Gestore del circuito lato server per acquisire gli utenti per i servizi personalizzati

Usare un CircuitHandler oggetto per acquisire un utente dall'oggetto AuthenticationStateProvider e impostare tale utente in un servizio. Per altre informazioni e codice di esempio, vedere ASP.NET Scenari di sicurezza aggiuntivi e Blazor Web App lato server Core.

Chiusura di circuiti quando non sono presenti componenti Interactive Server rimanenti

I componenti di Interactive Server gestiscono gli eventi dell'interfaccia utente Web usando una connessione in tempo reale con il browser denominato circuito. Un circuito e lo stato associato vengono creati quando viene eseguito il rendering di un componente Interactive Server radice. Il circuito viene chiuso quando nella pagina non sono presenti componenti Interactive Server rimanenti, che liberano risorse server.

IHttpContextAccessor/HttpContext nei Razor componenti

IHttpContextAccessor è necessario evitare il rendering interattivo perché non è disponibile un valore valido HttpContext .

IHttpContextAccessor può essere usato per i componenti di cui viene eseguito il rendering statico nel server. Tuttavia, è consigliabile evitarlo, se possibile.

HttpContext può essere usato come parametro a catena solo nei componenti radice sottoposti a rendering statico per attività generali, ad esempio l'ispezione e la modifica di intestazioni o altre proprietà nel App componente (Components/App.razor). Il valore è sempre null per il rendering interattivo.

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

Per gli scenari in cui HttpContext è necessario nei componenti interattivi, è consigliabile fluire i dati tramite lo stato del componente persistente dal server. Per altre informazioni, vedere ASP.NET core server-side e Blazor Web App altri scenari di sicurezza.

Non usare IHttpContextAccessor/HttpContext direttamente o indirettamente nei Razor componenti delle app lato Blazor server. Blazor le app vengono eseguite all'esterno del contesto della pipeline core ASP.NET. Non HttpContext è garantito che sia disponibile all'interno di IHttpContextAccessore HttpContext non sia garantito che contenga il contesto che ha avviato l'app Blazor .

L'approccio consigliato per passare lo stato della richiesta all'app consiste nell'usare Blazor i parametri del componente radice durante il rendering iniziale dell'app. In alternativa, l'app può copiare i dati in un servizio con ambito nell'evento del ciclo di vita di inizializzazione del componente radice da usare nell'app. Per altre informazioni, vedere ASP.NET core server-side e Blazor Web App altri scenari di sicurezza.

Un aspetto critico della sicurezza lato Blazor server è che l'utente collegato a un determinato circuito potrebbe essere aggiornato a un certo punto dopo che il Blazor circuito è stato stabilito, ma IHttpContextAccessornon viene aggiornato. Per altre informazioni sulla gestione di questa situazione con i servizi personalizzati, vedere ASP.NET scenari di sicurezza aggiuntivi e Blazor Web App lato server core.

Risorse aggiuntive sul lato server