Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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 Core SignalR, vedere gli argomenti nell'area Panoramica di ASP.NET Core SignalR della documentazione, in particolare la configurazione di ASP.NET Core SignalR.
Le app lato server usano ASP.NET Core SignalR per comunicare con il browser. SignalRLe condizioni di hosting e ridimensionamento si applicano alle app server.
Blazor funziona meglio quando si usano WebSocket come SignalR trasporto a causa di bassa latenza, affidabilità e sicurezza. Long Polling viene utilizzato da SignalR quando WebSockets non è disponibile o quando l'app è configurata esplicitamente per utilizzarlo.
Servizio di Azure SignalR con riconnessione con stato
Il servizio Azure SignalR con SDK v1.26.1 o versione successiva supporta SignalR riconnessione con stato (WithStatefulReconnect).
Compressione WebSocket per componenti server interattivi
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 direttiva CSP (Content Security Policy) impostata su
frame-ancestors
, che è l'impostazione predefinita e consente solo l'incorporamento dell'app in un'self'
dell'origine da cui viene servita l'app, quando la compressione è abilitata o quando viene fornita una configurazione per il contesto WebSocket.
La configurazione predefinita del CSP frame-ancestors
può essere modificata impostando il valore di ContentSecurityFrameAncestorsPolicy su null
se si desidera configurare il CSP in modo centralizzato o per una politica ancora più rigorosa. Quando il frame-ancestors
CSP viene gestito in modo centralizzato, è necessario prestare attenzione ad applicare un criterio ogni volta che viene eseguito il rendering del primo documento. Non è consigliabile rimuovere completamente i criteri, perché rende l'app vulnerabile agli attacchi. Per altre informazioni, vedere Applicare criteri di sicurezza del contenuto per ASP.NET CoreBlazor e la Guida MDN CSP.
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 CSP per gli antenati del 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, configura un CSP più rigoroso con un valore frame-ancestors
(sono necessarie virgolette singole), che consente la compressione WebSocket ma impedisce ai browser di incorporare l'app in un 'none'
oggetto:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Quando la compressione è abilitata, rimuovere il CSP impostando frame-ancestors
su ContentSecurityFrameAncestorsPolicy. Questo scenario è consigliato solo per le app che impostano la politica di sicurezza dei contenuti in modo centralizzato:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = null)
Importante
I browser applicano direttive CSP da più intestazioni CSP utilizzando il criterio di direttiva 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
Valori supportati:'none'
, 'self'
Le opzioni aggiuntive includono la specifica di una o più origini host e una o più origini di schema.
Per le implicazioni relative alla sicurezza, vedere Linee guida per la mitigazione delle minacce per ASP.NET Core nel rendering interattivo lato serverBlazor. Per altre informazioni, vedere Applicare criteri di sicurezza del contenuto per ASP.NET Core Blazor e CSP: frame-ancestors
(documentazione mdn).
Disabilitare la compressione delle risposte per Ricaricamento rapido
Quando si utilizza Hot Reload, disabilitare il middleware di compressione della risposta nell'ambiente Development
. 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 cross-origin lato cliente 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 nelle richieste cross-origin 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 creata una connessione hub, assegnare HttpMessageHandler all'opzione 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'indirizzo URI assoluto della connessione hub a /chathub
. L'URI può anche essere impostato tramite una stringa, ad esempio https://signalr.example.com
o tramite la configurazione.
Navigation
è un oggetto iniettato NavigationManager.
Per altre informazioni, vedere configurazione di ASP.NET CoreSignalR.
Rendering lato client
Se il prerendering è configurato, il prerendering si verifica prima che venga stabilita la connessione del client al server. Per altre informazioni, vedere componenti Prerender ASP.NET CoreRazor.
Se il prerendering è configurato, il prerendering si verifica prima che venga stabilita la connessione del 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 prerenderizzata di grandi dimensioni può superare il limite di dimensione dei messaggi del circuito Blazor, causando il seguente risultato:
- 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 prerenderizzato.
- Aumentare il limite della dimensione del messaggio. AVVISO: l'aumento del limite può aumentare il rischio di attacchi Denial of Service (DoS).
Risorse aggiuntive sul lato client
- Proteggere un SignalR hub
- Panoramica di ASP.NET Core SignalR
- Configurazione SignalR di ASP.NET Core
-
Blazor repository di esempio su GitHub (
dotnet/blazor-samples
) (come scaricare)
Usare l'affinità di sessione (sessioni permanenti) per l'hosting di web farm sul lato server
Quando sono in uso più server back-end, l'app deve implementare l'affinità di sessione, nota anche come 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 una Web farm:
Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed.
Per ulteriori informazioni sull'affinità di sessione con l'hosting di Azure App Service, vedere Host e distribuire le app server-side di ASP.NET CoreBlazor.
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 agevolare l'espansione delle connessioni su larga scala.
- Gestire la distribuzione globale.
Per ulteriori informazioni, consultare
Opzioni di gestione del circuito lato server
Configurare il circuito con CircuitOptions. Visualizzare i valori predefiniti nell'origine di riferimento.
Nota
I collegamenti della documentazione al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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).
Leggi o imposta le opzioni nel file Program
utilizzando un delegato delle opzioni per AddInteractiveServerComponents. Il {OPTION}
segnaposto rappresenta l'opzione e il {VALUE}
segnaposto è il valore .
Nel file Program
:
builder.Services.AddRazorComponents().AddInteractiveServerComponents(options =>
{
options.{OPTION} = {VALUE};
});
Leggi o imposta le opzioni nel file Program
utilizzando un delegato delle opzioni per 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. Visualizza le impostazioni predefinite per le opzioni del contesto di connessione hub nella fonte 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 al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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 MaximumParallelInvocationsPerClient impostato su 1, che è 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 Gestire la memoria in app ASP.NET Core lato server distribuiteBlazor.
Blazor opzioni hub
Configurare le opzioni MapBlazorHub per controllare HttpConnectionDispatcherOptions del hub Blazor. Consulta 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 al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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 app.MapBlazorHub
dopo la chiamata a app.MapRazorComponents
nel file Program
dell'app.
app.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
La configurazione dell'hub usato da AddInteractiveServerRenderMode con MapBlazorHub ha esito negativo a causa di un AmbiguousMatchException
.
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
Per risolvere il problema per le app destinate a .NET 8, assegnare una precedenza più alta all'hub personalizzato Blazor configurato utilizzando il metodo WithOrder.
app.MapBlazorHub(options =>
{
options.CloseOnAuthenticationExpiration = true;
}).WithOrder(-1);
Per ulteriori informazioni, vedi le seguenti risorse:
-
La configurazione di MapBlazorHub in NET8 genera un'eccezione di tipo The request matched multiple endpoints exception (
dotnet/aspnetcore
#51698) - I tentativi di eseguire il mapping di più punti di ingresso Blazor con MapBlazorHub causano un errore di percorso ambiguo. Questo ha funzionato con net7 (
dotnet/aspnetcore
#52156)
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 consentite per i messaggi in arrivo SignalR nei metodi hub sono limitate da HubOptions.MaximumReceiveMessageSize (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 Trace, 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 la registrazione lato server è impostata su SignalR o Trace, viene visualizzato un errore 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 interagiscono male con il Garbage Collector, causando ulteriori penalizzazioni delle 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 utilizzano tecniche simili al InputFile
componente, vedere l'app di esempio Binary Submit e il campione del componente BlazorInputLargeTextArea
.
Nota
I collegamenti della documentazione al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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 modelli che elaborano payload di grandi dimensioni possono anche usare direttamente l'interoperabilità di streaming. Per altre informazioni, vedere Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor. Per un esempio di moduli che trasmettono i dati <textarea>
al server, vedere Risolvere i problemi nei moduli di 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 interagiscono male con il Garbage Collector, causando ulteriori penalizzazioni delle 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 usando tecniche simili al componente InputFile
, vedi l'applicazione di esempio Binary Submit e l'esempio del componente BlazorInputLargeTextArea
.
Nota
I collegamenti della documentazione al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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 modelli che elaborano payload di grandi dimensioni possono anche usare direttamente l'interoperabilità di streaming. Per altre informazioni, vedere Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor. Per un esempio di moduli che trasmette dati <textarea>
in un'app Blazor Server, vedere Risoluzione dei problemi di ASP.NET Core Blazor moduli.
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 interagiscono male con il Garbage Collector, causando ulteriori penalizzazioni delle 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 codice C# JS.
- 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 codice C# JS.
- 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
Nel file Program
, chiamare MapBlazorHub per eseguire il mapping di 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:
Se la riconnessione non riesce, all'utente viene richiesto di riprovare o ricaricare la pagina:
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 ulteriori informazioni, vedere gestione dello stato di ASP.NET CoreBlazor.
Per creare elementi dell'interfaccia utente che tengono traccia dello stato di riconnessione, la tabella seguente descrive:
- Set di classi CSS
components-reconnect-*
(classe Css colonna) impostate o annullate da Blazor su un elemento con unid
dicomponents-reconnect-modal
. - Evento
components-reconnect-state-changed
(colonnaEvent) che indica una modifica dello stato di riconnessione.
Classe CSS | Evento | Indica... |
---|---|---|
components-reconnect-show |
show |
Connessione persa. Il client sta tentando di riconnettersi. Viene visualizzato il modale di riconnessione. |
components-reconnect-hide |
hide |
Viene ristabilita una connessione attiva al server. Il modello di riconnessione è chiuso. |
components-reconnect-retrying |
retrying |
Il client sta tentando di riconnettersi. |
components-reconnect-failed |
failed |
La riconnessione non è riuscita, probabilmente a causa di un errore di rete. |
components-reconnect-rejected |
rejected |
Riconnessione rifiutata. |
Quando lo stato di riconnessione in components-reconnect-state-changed
è failed
, chiamare Blazor.reconnect()
in JavaScript per tentare la riconnessione.
Quando la modifica dello stato di riconnessione è rejected
, 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 crash 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.
Lo sviluppatore aggiunge un listener di eventi nell'elemento modale di riconnessione per monitorare e reagire alle modifiche dello stato di riconnessione, come illustrato nell'esempio seguente:
const reconnectModal = document.getElementById("components-reconnect-modal");
reconnectModal.addEventListener("components-reconnect-state-changed",
handleReconnectStateChanged);
function handleReconnectStateChanged(event) {
if (event.detail.state === "show") {
reconnectModal.showModal();
} else if (event.detail.state === "hide") {
reconnectModal.close();
} else if (event.detail.state === "failed") {
Blazor.reconnect();
} else if (event.detail.state === "rejected") {
location.reload();
}
}
Un elemento con id
pari a components-reconnect-max-retries
mostra il massimo numero di tentativi di riconnessione:
<span id="components-reconnect-max-retries"></span>
Un elemento con un id
di components-reconnect-current-attempt
visualizza il tentativo di riconnessione corrente:
<span id="components-reconnect-current-attempt"></span>
Un elemento con id
pari a components-seconds-to-next-attempt
visualizza il numero di secondi al tentativo di riconnessione successivo.
<span id="components-seconds-to-next-attempt"></span>
Il modello di progetto Blazor Web App include un componente ReconnectModal
(Layout/ReconnectModal.razor
) con foglio di stile collocato e file JavaScript (ReconnectModal.razor.css
, ReconnectModal.razor.js
) che possono essere personalizzati in base alle esigenze. Questi file possono essere esaminati nell'origine di riferimento ASP.NET Core o esaminando un'app creata dal modello di progetto Blazor Web App. Il componente viene aggiunto al progetto quando il progetto viene creato in Visual Studio con modalità di rendering interattiva impostata su Server o Automatico o creato con l'interfaccia della riga di comando di .NET con l'opzione --interactivity server
(impostazione predefinita) o --interactivity auto
.
Nota
I collegamenti della documentazione al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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).
Per personalizzare l'interfaccia utente, definire un singolo elemento contenente un id
di tipo components-reconnect-modal
nei contenuti dell'elemento <body>
. Nell'esempio seguente, l'elemento viene inserito nel componente App
.
App.razor
:
Per personalizzare l'interfaccia utente, definire un singolo elemento contenente un id
di tipo components-reconnect-modal
nei contenuti 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 contenente un id
di tipo components-reconnect-modal
nei contenuti 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 contenente un id
di tipo components-reconnect-modal
nei contenuti 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 l'app esegue il rendering di più elementi con un id
di components-reconnect-modal
, solo il primo elemento renderizzato 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 la finestra 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:
|
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
impostato su 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 componenti Prerender ASP.NET CoreRazor.
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 sul 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, come eventi dell'interfaccia utente o chiamate di interoperabilità JavaScript e .NET.
Ad esempio, puoi usare un gestore di attività del circuito per rilevare se il client è inattivo e registrarne l'ID del 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 ambito Blazor da altri ambiti non Blazor di inserimento delle dipendenze (DI). Per altre informazioni ed esempi, vedere:
Blazor avvio
Configura l'avvio manuale del circuito di BlazorSignalR nel file App.razor
di un oggetto Blazor Web App.
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 loblazor.*.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 ulteriori informazioni, tra cui come inizializzare Blazor quando il documento è pronto e come concatenare a un JS Promise
, consulta ASP.NET Core Blazor startup.
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 forzate, ad esempio quando un client scollega il proprio 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, assegnare un valore inferiore a5000
(5 secondi) farà comunque in modo che esegua 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 la struttura del progetto
Quando si crea una connessione hub in un componente, impostare ServerTimeout (impostazione predefinita: 30 secondi) e KeepAliveInterval (impostazione predefinita: 15 secondi) in HubConnectionBuilder. Impostare il valore di HandshakeTimeout (predefinito: 15 secondi) sull'elemento costruito 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
: Il tempo di attesa 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 forzate, ad esempio quando un client scollega il proprio 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, assegnare un valore inferiore a5000
(5 secondi) farà comunque in modo che esegua 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 la struttura del progetto
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:
- Eseguire e distribuire applicazioni ASP.NET Core lato serverBlazor
- Ospitare e distribuire ASP.NET Core Blazor WebAssembly
Modificare il gestore di riconnessione lato server
Gli eventi di connessione del circuito 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 cadute 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 la struttura del progetto
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 la struttura del progetto
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 la struttura del progetto
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 al codice sorgente di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare il menu 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'applicazione distribuita visualizza spesso l'interfaccia utente di riconnessione a causa di timeout di ping causati dalla latenza della rete interna o di Internet e si desidera aumentare il ritardo.
- Un'app dovrebbe segnalare tempestivamente agli utenti che la connessione è stata interrotta, e si desidera ridurre il ritardo.
La tempistica con cui appare l'interfaccia utente di riconnessione è influenzata dalla modifica dell'intervallo Keep-Alive e dei timeout sul client. L'interfaccia utente di riconnessione viene visualizzata quando viene raggiunto il timeout del server nel client (withServerTimeout
sezione 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.
Il ClientTimeoutInterval e il HandshakeTimeout possono essere aumentati, e il KeepAliveInterval può 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 time-out e Keep-Alive nel client.
Nell'esempio seguente :
- Il ClientTimeoutInterval è aumentato a 60 secondi (valore predefinito: 30 secondi).
- Il HandshakeTimeout viene aumentato a 30 secondi (valore predefinito: 15 secondi).
- Il KeepAliveInterval non è impostato nel codice dello sviluppatore e utilizza 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 versioni successive) nel file Program
del progetto server:
builder.Services.AddRazorComponents().AddInteractiveServerComponents()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
Blazor Server nel Program
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 time-out 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 (WithServerTimeout, impostazione predefinita: 30 secondi) su HubConnectionBuilder. Impostare il valore di HandshakeTimeout (predefinito: 15 secondi) sull'elemento costruito 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 è basato sul componente Index
nel tutorial con SignalRBlazor. 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 time-out 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 il ServerTimeout (impostazione predefinita: 30 secondi) e il HandshakeTimeout (impostazione predefinita: 15 secondi) sull'oggetto creato HubConnection. Verificare che i timeout siano almeno due volte l'intervallo Keep-Alive. Verificare che l'intervallo Keep-Alive corrisponda tra server e client.
L'esempio seguente è basato sul componente Index
nel tutorial con SignalRBlazor.
ServerTimeout viene aumentato a 60 secondi e HandshakeTimeout viene 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 il circuito di BlazorSignalR dal client
Blazor Il circuito di 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 da CircuitHandler e registrando la classe nel contenitore dei servizi dell'app. L'esempio seguente di un gestore di 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 utilizzando l'Inversione delle Dipendenze (DI). 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 gestire le eccezioni nel codice di un gestore o nei metodi invocati, racchiudere il codice in una o più istruzioni try-catch
con la gestione e la registrazione degli errori.
Quando un circuito termina perché un utente si è disconnesso e il framework sta ripulendo lo stato del circuito, il framework elimina l'ambito di DI del circuito. L'eliminazione dell'ambito elimina tutti i servizi DI con ambito di circuito che implementano System.IDisposable. Se un servizio DI 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 CircuitHandler per catturare un utente da AuthenticationStateProvider e assegnare tale utente a un servizio. Per ulteriori informazioni e codice di esempio, vedere ASP.NET Core lato server e Blazor Web App scenari di sicurezza aggiuntivi.
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 il suo stato associato vengono creati quando un componente principale del Server Interattivo è renderizzato. Il circuito viene chiuso quando nella pagina non sono presenti componenti Interactive Server rimanenti, che liberano risorse server.
Avviare il circuito SignalR in un URL diverso
Impedire l'avvio automatico dell'app aggiungendo autostart="false"
al tag Blazor<script>
(posizione dello script di avvio Blazor). Impostare manualmente l'URL del circuito usando Blazor.start
. Negli esempi seguenti viene usato il percorso /signalr
.
Blazor Web App:
- <script src="_framework/blazor.web.js"></script>
+ <script src="_framework/blazor.web.js" autostart="false"></script>
+ <script>
+ Blazor.start({
+ circuit: {
+ configureSignalR: builder => builder.withUrl("/signalr")
+ },
+ });
+ </script>
Blazor Server:
- <script src="_framework/blazor.server.js"></script>
+ <script src="_framework/blazor.server.js" autostart="false"></script>
+ <script>
+ Blazor.start({
+ configureSignalR: builder => builder.withUrl("/signalr")
+ });
+ </script>
Aggiungere la seguente chiamata MapBlazorHub con il percorso dell'hub alla pipeline di elaborazione del middleware nel file Program
dell'applicazione server.
Blazor Web App:
app.MapBlazorHub("/signalr");
Blazor Server:
Lasciare la chiamata esistente a MapBlazorHub nel file e aggiungere una nuova chiamata a MapBlazorHub con il percorso:
app.MapBlazorHub();
+ app.MapBlazorHub("/signalr");
Rappresentazione per l'autenticazione di Windows
Le connessioni hub autenticate (HubConnection) vengono create con UseDefaultCredentials per indicare l'uso delle credenziali predefinite per le richieste HTTP. Per altre informazioni, vedere Autenticazione e autorizzazione in ASP.NET Core SignalR.
Quando l'app è in esecuzione in IIS Express come utente connesso in Autenticazione di Windows, probabilmente l'account personale o aziendale dell'utente, le credenziali predefinite sono quelle dell'utente connesso.
Quando l'app viene pubblicata in IIS, l'app viene eseguita nel pool di applicazioni Identity. Il HubConnection si connette come account "utente" IIS che ospita l'app, non l'utente che accede alla pagina.
Implementare impersonificazione con il HubConnection per usare l'identità dell'utente che naviga.
Nell'esempio seguente :
- L'utente del provider di stato di autenticazione viene convertito in un WindowsIdentity.
- Il token di accesso dell'identità viene passato a WindowsIdentity.RunImpersonatedAsync con il codice che costruisce e avvia il HubConnection.
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState?.User.Identity is not null)
{
var user = authState.User.Identity as WindowsIdentity;
if (user is not null)
{
await WindowsIdentity.RunImpersonatedAsync(user.AccessToken,
async () =>
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavManager.ToAbsoluteUri("/hub"), config =>
{
config.UseDefaultCredentials = true;
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string>("name", userName =>
{
name = userName;
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
});
}
}
}
Nel codice precedente, NavManager
è un NavigationManagere AuthenticationStateProvider
è un'istanza di servizio AuthenticationStateProvider (AuthenticationStateProvider
documentazione).
Risorse aggiuntive sul lato server
- Linee guida per l'host e la distribuzione: configurazione sul lato serverSignalR
- Panoramica di ASP.NET Core SignalR
- Configurazione SignalR di ASP.NET Core
- Documentazione sulla sicurezza lato server
- IHttpContextAccessor/HttpContext nelle app ASP.NET Core Blazor
- Eventi di riconnessione lato server ed eventi del ciclo di vita dei componenti
- Che cos'è il servizio di Azure SignalR ?
- Guida alle prestazioni per il servizio di Azure SignalR
- Pubblicare un'app core SignalR ASP.NET nel servizio app Azure
-
Blazor repository di esempio su GitHub (
dotnet/blazor-samples
) (come scaricare)