ASP.NET client JavaScript core SignalR
Di Rachel Appel
La libreria client JavaScript di ASP.NET Core SignalR consente agli sviluppatori di chiamare il codice dell'hub sul lato SignalR server.
Installare il SignalR pacchetto client
La SignalR libreria client JavaScript viene distribuita come pacchetto npm . Le sezioni seguenti illustrano diversi modi per installare la libreria client.
Eseguire l'installazione con npm
Eseguire i comandi seguenti da Gestione pacchetti Console:
npm init -y
npm install @microsoft/signalr
npm installa il contenuto del pacchetto nella cartella node_modules\@microsoft\\signalrdist\browser . Creare la cartella wwwroot/lib/signalr . Copiare il signalr.js
file nella cartella wwwroot/lib/signalr .
Fare riferimento al SignalR client JavaScript nell'elemento <script>
. Ad esempio:
<script src="~/lib/signalr/signalr.js"></script>
Usare un rete per la distribuzione di contenuti (rete CDN)
Per usare la libreria client senza il prerequisito npm, fare riferimento a una copia ospitata dalla rete CDN della libreria client. Ad esempio:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
La libreria client è disponibile nelle reti CDN seguenti:
Eseguire l'installazione con LibMan
LibMan può essere usato per installare file di libreria client specifici dalla libreria client ospitata dalla rete CDN. Ad esempio, aggiungere solo il file JavaScript minimizzato al progetto. Per informazioni dettagliate su questo approccio, vedere Aggiungere la SignalR libreria client.
Connettersi a un hub
Il codice seguente crea e avvia una connessione. Il nome dell'hub non fa distinzione tra maiuscole e minuscole:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
Connessioni tra origini (CORS)
In genere, i browser caricano le connessioni dallo stesso dominio della pagina richiesta. Tuttavia, in alcuni casi è necessaria una connessione a un altro dominio.
Quando si effettuano richieste tra domini, il codice client deve usare un URL assoluto anziché un URL relativo. Per le richieste tra domini, passare .withUrl("/chathub")
a .withUrl("https://{App domain name}/chathub")
.
Per impedire a un sito dannoso di leggere dati sensibili da un altro sito, le connessioni tra le origini sono disabilitate per impostazione predefinita. Per consentire una richiesta tra le origini, abilitare CORS:
using SignalRChat.Hubs;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("https://example.com")
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// UseCors must be called before MapHub.
app.UseCors();
app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
UseCors deve essere chiamato prima di chiamare MapHub.
Chiamare i metodi dell'hub dal client
I client JavaScript chiamano metodi pubblici negli hub tramite il metodo invoke di HubConnection. Il invoke
metodo accetta:
- Nome del metodo hub.
- Qualsiasi argomento definito nel metodo hub.
Nel codice evidenziato seguente il nome del metodo nell'hub è SendMessage
. Il secondo e il terzo argomento passati per eseguire invoke
il mapping agli argomenti e message
del user
metodo hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
La chiamata dei metodi hub da un client è supportata solo quando si usa il servizio di Azure SignalR in modalità predefinita . Per altre informazioni, vedere Domande frequenti (azure-signalr repository GitHub).
Il invoke
metodo restituisce un oggetto JavaScript Promise
. L'oggetto Promise
viene risolto con il valore restituito ( se presente) quando il metodo sul server restituisce . Se il metodo nel server genera un errore, l'oggetto Promise
viene rifiutato con il messaggio di errore. Usare async
e o i Promise
then
metodi e await
catch
per gestire questi casi.
I client JavaScript possono anche chiamare metodi pubblici negli hub tramite il metodo send di HubConnection
. A differenza del invoke
metodo , il send
metodo non attende una risposta dal server. Il send
metodo restituisce un oggetto JavaScript Promise
. L'oggetto Promise
viene risolto quando il messaggio è stato inviato al server. Se si verifica un errore durante l'invio del messaggio, l'oggetto Promise
viene rifiutato con il messaggio di errore. Usare async
e o i Promise
then
metodi e await
catch
per gestire questi casi.
L'uso send
non attende finché il server non ha ricevuto il messaggio. Di conseguenza, non è possibile restituire dati o errori dal server.
Chiamare i metodi client dall'hub
Per ricevere messaggi dall'hub, definire un metodo usando il metodo on di HubConnection
.
- Nome del metodo client JavaScript.
- Gli argomenti passati dall'hub al metodo .
Nell'esempio seguente il nome del metodo è ReceiveMessage
. I nomi degli argomenti sono user
e message
:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
Il codice precedente in connection.on
viene eseguito quando il codice sul lato server lo chiama usando il SendAsync metodo :
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
SignalR determina il metodo client da chiamare associando il nome del metodo e gli argomenti definiti in SendAsync
e connection.on
.
Una procedura consigliata consiste nel chiamare il metodo start su HubConnection
dopo on
. In questo modo, i gestori vengono registrati prima che vengano ricevuti messaggi.
Registrazione e gestione degli errori
Usare console.error
per generare errori nella console del browser quando il client non riesce a connettersi o inviare un messaggio:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configurare la traccia dei log sul lato client passando un logger e il tipo di evento da registrare quando viene stabilita la connessione. I messaggi vengono registrati con il livello di log specificato e superiore. I livelli di log disponibili sono i seguenti:
signalR.LogLevel.Error
: messaggi di errore. Registra solo iError
messaggi.signalR.LogLevel.Warning
: messaggi di avviso relativi a potenziali errori. Registra iWarning
messaggi eError
.signalR.LogLevel.Information
: messaggi di stato senza errori. RegistraInformation
i messaggi ,Warning
eError
.signalR.LogLevel.Trace
: messaggi di traccia. Registra tutti gli elementi, inclusi i dati trasportati tra hub e client.
Usare il metodo configureLogging in HubConnectionBuilder per configurare il livello di log. I messaggi vengono registrati nella console del browser:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Riconnettere i client
Riconnettersi automaticamente
Il client JavaScript per può essere configurato per SignalR riconnettersi automaticamente usando il metodo WithAutomaticReconnect in HubConnectionBuilder. Per impostazione predefinita, la riconnessione non verrà riconnessa automaticamente.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Senza parametri, WithAutomaticReconnect configura il client per attendere rispettivamente 0, 2, 10 e 30 secondi prima di provare ogni tentativo di riconnessione. Dopo quattro tentativi non riusciti, smette di tentare di riconnettersi.
Prima di avviare eventuali tentativi di riconnessione, :HubConnection
- Esegue la transizione allo
HubConnectionState.Reconnecting
stato e ne attiva ionreconnecting
callback. - Non esegue la transizione allo
Disconnected
stato e attiva ionclose
callback come unHubConnection
senza riconnessione automatica configurata.
L'approccio di riconnessione offre un'opportunità per:
- Avvisa gli utenti che la connessione è stata persa.
- Disabilitare gli elementi dell'interfaccia utente.
connection.onreconnecting(error => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messageList").appendChild(li);
});
Se il client si riconnette correttamente entro i primi quattro tentativi, torna HubConnection
allo Connected
stato e genera i onreconnected
relativi callback. In questo modo è possibile informare gli utenti che la connessione è stata ristabilita.
Poiché la connessione sembra completamente nuova per il server, viene fornito onreconnected
un nuovo connectionId
al callback.
Il onreconnected
parametro del connectionId
callback non è definito se è configurato per ignorare la HubConnection
negoziazione.
connection.onreconnected(connectionId => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messageList").appendChild(li);
});
withAutomaticReconnect
non configurerà per HubConnection
ripetere gli errori di avvio iniziale, quindi gli errori di avvio devono essere gestiti manualmente:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("SignalR Connected.");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
Se il client non si riconnette correttamente entro i primi quattro tentativi, passa HubConnection
allo Disconnected
stato e ne attiva i callback onclose . In questo modo è possibile informare gli utenti:
- La connessione è stata persa definitivamente.
- Provare ad aggiornare la pagina:
connection.onclose(error => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messageList").appendChild(li);
});
Per configurare un numero personalizzato di tentativi di riconnessione prima di disconnettersi o modificare l'intervallo di riconnessione, withAutomaticReconnect
accetta una matrice di numeri che rappresenta il ritardo in millisecondi di attesa prima di avviare ogni tentativo di riconnessione.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
Nell'esempio precedente viene configurato l'oggetto HubConnection
per avviare il tentativo di riconnessione immediatamente dopo la perdita della connessione. La configurazione predefinita attende anche zero secondi per tentare di riconnettersi.
Se il primo tentativo di riconnessione non riesce, il secondo tentativo di riconnessione viene avviato immediatamente anziché attendere 2 secondi usando la configurazione predefinita.
Se il secondo tentativo di riconnessione non riesce, il terzo tentativo di riconnessione inizia in 10 secondi, che corrisponde alla configurazione predefinita.
L'intervallo di riconnessione configurato differisce dal comportamento predefinito arrestando dopo il terzo tentativo di riconnessione invece di provare un altro tentativo di riconnessione in altri 30 secondi.
Per un maggiore controllo sull'intervallo e sul numero di tentativi di riconnessione automatica, withAutomaticReconnect
accetta un oggetto che implementa l'interfaccia IRetryPolicy
, che ha un singolo metodo denominato nextRetryDelayInMilliseconds
.
nextRetryDelayInMilliseconds
accetta un singolo argomento con il tipo RetryContext
. Ha RetryContext
tre proprietà: previousRetryCount
elapsedMilliseconds
e retryReason
che sono rispettivamente , number
e number
.Error
Prima del primo tentativo di riconnessione, entrambi previousRetryCount
e elapsedMilliseconds
saranno zero e retryReason
sarà l'errore che ha causato la perdita della connessione. Dopo ogni tentativo di ripetizione non riuscito, previousRetryCount
verrà incrementato di uno, elapsedMilliseconds
verrà aggiornato in modo da riflettere la quantità di tempo impiegato per la riconnessione finora in millisecondi e sarà retryReason
l'errore che ha causato l'ultimo tentativo di riconnessione non riuscito.
nextRetryDelayInMilliseconds
deve restituire un numero che rappresenta il numero di millisecondi di attesa prima del tentativo di riconnessione successivo o null
se deve interrompere la HubConnection
riconnessione.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
In alternativa, è possibile scrivere il codice che riconnette il client manualmente, come illustrato nella sezione seguente.
Riconnettersi manualmente
Il codice seguente illustra un tipico approccio di riconnessione manuale:
- Viene creata una funzione (in questo caso, la
start
funzione ) per avviare la connessione. - Chiamare la
start
funzione nel gestore eventi dellaonclose
connessione.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
Le implementazioni di produzione usano in genere un back-off esponenziale o riprovano un numero specificato di volte.
Scheda di sospensione del browser
Alcuni browser hanno una funzionalità di blocco o sospensione delle schede per ridurre l'utilizzo delle risorse del computer per le schede inattive. Ciò può causare SignalR la chiusura delle connessioni e può causare un'esperienza utente indesiderata. I browser usano euristica per capire se una scheda deve essere messa in sospensione, ad esempio:
- Riproduzione di audio
- Blocco Web
- Tenere premuto un
IndexedDB
blocco - Connessione a un dispositivo USB
- Acquisizione di video o audio
- Mirroring
- Acquisizione di una finestra o di una visualizzazione
L'euristica del browser può cambiare nel tempo e può variare tra i browser. Controllare la matrice di supporto e individuare il metodo più adatto per gli scenari.
Per evitare di mettere in sospensione un'app, l'app deve attivare una delle euristiche usate dal browser.
Nell'esempio di codice seguente viene illustrato come usare un blocco Web per mantenere attiva una scheda ed evitare una chiusura imprevista della connessione.
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
const promise = new Promise((res) => {
lockResolver = res;
});
navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
return promise;
});
}
Per l'esempio di codice precedente:
- I blocchi Web sono sperimentali. Il controllo condizionale conferma che il browser supporta i blocchi Web.
- Il sistema di risoluzione delle promesse,
lockResolver
, viene archiviato in modo che il blocco possa essere rilasciato quando è accettabile che la scheda sia in sospensione. - Quando si chiude la connessione, il blocco viene rilasciato chiamando
lockResolver()
. Quando il blocco viene rilasciato, la scheda è autorizzata a dormire.
Risorse aggiuntive
- Visualizzare o scaricare il codice di esempio (procedura per il download)
- Informazioni di riferimento sull'API JavaScript
- Esercitazione su JavaScript
- Esercitazione su WebPack e TypeScript
- Hub
- Client .NET
- Pubblicare in Azure
- Richieste tra origini (CORS)
- Documentazione serverless del servizio di Azure SignalR
- Risolvere gli errori di connessione
Di Rachel Appel
La libreria client JavaScript di ASP.NET Core SignalR consente agli sviluppatori di chiamare il codice dell'hub sul lato server.
Visualizzare o scaricare il codice di esempio (procedura per il download)
Installare il SignalR pacchetto client
La SignalR libreria client JavaScript viene distribuita come pacchetto npm . Le sezioni seguenti illustrano diversi modi per installare la libreria client.
Eseguire l'installazione con npm
Per Visual Studio, eseguire i comandi seguenti dalla console di Gestione pacchetti nella cartella radice. Per Visual Studio Code, eseguire i comandi seguenti dal terminale integrato.
npm init -y
npm install @microsoft/signalr
npm installa il contenuto del pacchetto nella cartella node_modules\@microsoft\\signalrdist\browser . Creare una nuova cartella denominata signalr nella cartella wwwroot\lib . Copiare il signalr.js
file nella cartella wwwroot\lib\signalr .
Fare riferimento al SignalR client JavaScript nell'elemento <script>
. Ad esempio:
<script src="~/lib/signalr/signalr.js"></script>
Usare un rete per la distribuzione di contenuti (rete CDN)
Per usare la libreria client senza il prerequisito npm, fare riferimento a una copia ospitata dalla rete CDN della libreria client. Ad esempio:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.js"></script>
La libreria client è disponibile nelle reti CDN seguenti:
Eseguire l'installazione con LibMan
LibMan può essere usato per installare file di libreria client specifici dalla libreria client ospitata dalla rete CDN. Ad esempio, aggiungere solo il file JavaScript minimizzato al progetto. Per informazioni dettagliate su questo approccio, vedere Aggiungere la SignalR libreria client.
Connettersi a un hub
Il codice seguente crea e avvia una connessione. Il nome dell'hub non fa distinzione tra maiuscole e minuscole:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
Connessioni tra origini
In genere, i browser caricano le connessioni dallo stesso dominio della pagina richiesta. Tuttavia, in alcuni casi è necessaria una connessione a un altro dominio.
Importante
Il codice client deve usare un URL assoluto anziché un URL relativo. Cambia .withUrl("/chathub")
in .withUrl("https://myappurl/chathub")
.
Per impedire a un sito dannoso di leggere dati sensibili da un altro sito, le connessioni tra le origini sono disabilitate per impostazione predefinita. Per consentire una richiesta tra le origini, abilitarla nella Startup
classe :
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalRChat.Hubs;
namespace SignalRChat
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("https://example.com")
.AllowCredentials();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
}
Chiamare i metodi dell'hub dal client
I client JavaScript chiamano metodi pubblici negli hub tramite il metodo invoke di HubConnection. Il invoke
metodo accetta:
- Nome del metodo hub.
- Qualsiasi argomento definito nel metodo hub.
Nell'esempio seguente il nome del metodo nell'hub è SendMessage
. Il secondo e il terzo argomento passati per eseguire invoke
il mapping agli argomenti e message
del user
metodo hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Nota
La chiamata dei metodi hub da un client è supportata solo quando si usa il servizio di Azure SignalR in modalità predefinita . Per altre informazioni, vedere Domande frequenti (azure-signalr repository GitHub).
Il invoke
metodo restituisce un oggetto JavaScript Promise
. L'oggetto Promise
viene risolto con il valore restituito ( se presente) quando il metodo sul server restituisce . Se il metodo nel server genera un errore, l'oggetto Promise
viene rifiutato con il messaggio di errore. Usare async
e o i Promise
then
metodi e await
catch
per gestire questi casi.
I client JavaScript possono anche chiamare metodi pubblici negli hub tramite il metodo send di HubConnection
. A differenza del invoke
metodo , il send
metodo non attende una risposta dal server. Il send
metodo restituisce un oggetto JavaScript Promise
. L'oggetto Promise
viene risolto quando il messaggio è stato inviato al server. Se si verifica un errore durante l'invio del messaggio, l'oggetto Promise
viene rifiutato con il messaggio di errore. Usare async
e o i Promise
then
metodi e await
catch
per gestire questi casi.
Nota
L'uso send
non attende finché il server non ha ricevuto il messaggio. Di conseguenza, non è possibile restituire dati o errori dal server.
Chiamare i metodi client dall'hub
Per ricevere messaggi dall'hub, definire un metodo usando il metodo on di HubConnection
.
- Nome del metodo client JavaScript.
- Gli argomenti passati dall'hub al metodo .
Nell'esempio seguente il nome del metodo è ReceiveMessage
. I nomi degli argomenti sono user
e message
:
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messageList").appendChild(li);
});
Il codice precedente in connection.on
viene eseguito quando il codice sul lato server lo chiama usando il SendAsync metodo :
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
SignalR determina il metodo client da chiamare associando il nome del metodo e gli argomenti definiti in SendAsync
e connection.on
.
Nota
Come procedura consigliata, chiamare il metodo start su HubConnection
dopo on
. In questo modo, i gestori vengono registrati prima della ricezione di eventuali messaggi.
Registrazione e gestione degli errori
Usare try
e con async
e await
catch
o il Promise
metodo per catch
gestire gli errori sul lato client. Usare console.error
per restituire gli errori nella console del browser:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configurare la traccia dei log sul lato client passando un logger e il tipo di evento da registrare quando viene stabilita la connessione. I messaggi vengono registrati con il livello di log specificato e superiore. I livelli di log disponibili sono i seguenti:
signalR.LogLevel.Error
: messaggi di errore. Registra solo iError
messaggi.signalR.LogLevel.Warning
: messaggi di avviso relativi a potenziali errori. Registra iWarning
messaggi eError
.signalR.LogLevel.Information
: messaggi di stato senza errori. RegistraInformation
i messaggi ,Warning
eError
.signalR.LogLevel.Trace
: messaggi di traccia. Registra tutti gli elementi, inclusi i dati trasportati tra hub e client.
Usare il metodo configureLogging in HubConnectionBuilder per configurare il livello di log. I messaggi vengono registrati nella console del browser:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.configureLogging(signalR.LogLevel.Information)
.build();
Riconnettere i client
Riconnettersi automaticamente
Il client JavaScript per può essere configurato per SignalR riconnettersi automaticamente usando il withAutomaticReconnect
metodo in HubConnectionBuilder. Per impostazione predefinita, la riconnessione non verrà riconnessa automaticamente.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect()
.build();
Senza parametri, withAutomaticReconnect()
configura il client per attendere rispettivamente 0, 2, 10 e 30 secondi prima di provare ogni tentativo di riconnessione, interrompendo dopo quattro tentativi non riusciti.
Prima di avviare qualsiasi tentativo di riconnessione, passerà HubConnection
allo HubConnectionState.Reconnecting
stato e attiverà onreconnecting
i relativi callback invece di passare allo Disconnected
stato e attivare i onclose
relativi callback come un HubConnection
senza riconnessione automatica configurata. In questo modo è possibile avvisare gli utenti che la connessione è stata persa e di disabilitare gli elementi dell'interfaccia utente.
connection.onreconnecting(error => {
console.assert(connection.state === signalR.HubConnectionState.Reconnecting);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection lost due to error "${error}". Reconnecting.`;
document.getElementById("messageList").appendChild(li);
});
Se il client si riconnette correttamente entro i primi quattro tentativi, HubConnection
tornerà allo Connected
stato e ne attiverà onreconnected
i callback. In questo modo è possibile informare gli utenti che la connessione è stata ristabilita.
Poiché la connessione sembra completamente nuova per il server, verrà fornito onreconnected
un nuovo connectionId
callback.
Avviso
Il onreconnected
parametro del connectionId
callback non sarà definito se è stato configurato per ignorare la HubConnection
negoziazione.
connection.onreconnected(connectionId => {
console.assert(connection.state === signalR.HubConnectionState.Connected);
document.getElementById("messageInput").disabled = false;
const li = document.createElement("li");
li.textContent = `Connection reestablished. Connected with connectionId "${connectionId}".`;
document.getElementById("messageList").appendChild(li);
});
withAutomaticReconnect()
non configurerà per HubConnection
ripetere gli errori di avvio iniziale, quindi gli errori di avvio devono essere gestiti manualmente:
async function start() {
try {
await connection.start();
console.assert(connection.state === signalR.HubConnectionState.Connected);
console.log("SignalR Connected.");
} catch (err) {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
console.log(err);
setTimeout(() => start(), 5000);
}
};
Se il client non si riconnette correttamente entro i primi quattro tentativi, HubConnection
passerà allo Disconnected
stato e ne attiverà i callback onclose . In questo modo è possibile informare gli utenti che la connessione è stata persa definitivamente e consiglia di aggiornare la pagina:
connection.onclose(error => {
console.assert(connection.state === signalR.HubConnectionState.Disconnected);
document.getElementById("messageInput").disabled = true;
const li = document.createElement("li");
li.textContent = `Connection closed due to error "${error}". Try refreshing this page to restart the connection.`;
document.getElementById("messageList").appendChild(li);
});
Per configurare un numero personalizzato di tentativi di riconnessione prima di disconnettersi o modificare l'intervallo di riconnessione, withAutomaticReconnect
accetta una matrice di numeri che rappresenta il ritardo in millisecondi di attesa prima di avviare ogni tentativo di riconnessione.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 0, 10000])
.build();
// .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
Nell'esempio precedente viene configurato l'oggetto HubConnection
per avviare il tentativo di riconnessione immediatamente dopo la perdita della connessione. Questo vale anche per la configurazione predefinita.
Se il primo tentativo di riconnessione ha esito negativo, il secondo tentativo di riconnessione verrà avviato immediatamente anziché attendere 2 secondi come nella configurazione predefinita.
Se il secondo tentativo di riconnessione ha esito negativo, il terzo tentativo di riconnessione verrà avviato in 10 secondi, come la configurazione predefinita.
Il comportamento personalizzato si differenzia quindi di nuovo dal comportamento predefinito arrestando dopo il terzo tentativo di riconnessione invece di provare un altro tentativo di riconnessione in altri 30 secondi come nella configurazione predefinita.
Se si vuole un maggiore controllo sulla tempistica e sul numero di tentativi di riconnessione automatica, withAutomaticReconnect
accetta un oggetto che implementa l'interfaccia IRetryPolicy
, che ha un singolo metodo denominato nextRetryDelayInMilliseconds
.
nextRetryDelayInMilliseconds
accetta un singolo argomento con il tipo RetryContext
. Ha RetryContext
tre proprietà: previousRetryCount
elapsedMilliseconds
e retryReason
che sono rispettivamente , number
e number
.Error
Prima del primo tentativo di riconnessione, entrambi previousRetryCount
e elapsedMilliseconds
saranno zero e retryReason
sarà l'errore che ha causato la perdita della connessione. Dopo ogni tentativo di ripetizione non riuscito, previousRetryCount
verrà incrementato di uno, elapsedMilliseconds
verrà aggiornato in modo da riflettere la quantità di tempo impiegato per la riconnessione finora in millisecondi e sarà retryReason
l'errore che ha causato l'ultimo tentativo di riconnessione non riuscito.
nextRetryDelayInMilliseconds
deve restituire un numero che rappresenta il numero di millisecondi di attesa prima del tentativo di riconnessione successivo o null
se deve interrompere la HubConnection
riconnessione.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
In alternativa, è possibile scrivere codice che riconnetterà il client manualmente, come illustrato in Riconnettersi manualmente.
Riconnettersi manualmente
Il codice seguente illustra un tipico approccio di riconnessione manuale:
- Viene creata una funzione (in questo caso, la
start
funzione ) per avviare la connessione. - Chiamare la
start
funzione nel gestore eventi dellaonclose
connessione.
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
Le implementazioni di produzione usano in genere un back-off esponenziale o riprovano un numero specificato di volte.
Scheda di sospensione del browser
Alcuni browser hanno una funzionalità di blocco o sospensione delle schede per ridurre l'utilizzo delle risorse del computer per le schede inattive. Ciò può causare SignalR la chiusura delle connessioni e può causare un'esperienza utente indesiderata. I browser usano euristica per capire se una scheda deve essere messa in sospensione, ad esempio:
- Riproduzione di audio
- Blocco Web
- Tenere premuto un
IndexedDB
blocco - Connessione a un dispositivo USB
- Acquisizione di video o audio
- Mirroring
- Acquisizione di una finestra o di una visualizzazione
Nota
Queste euristiche possono cambiare nel tempo o differire tra browser. Controllare la matrice di supporto e individuare il metodo più adatto per gli scenari.
Per evitare di mettere in sospensione un'app, l'app deve attivare una delle euristiche usate dal browser.
Nell'esempio di codice seguente viene illustrato come usare un blocco Web per mantenere attiva una scheda ed evitare una chiusura imprevista della connessione.
var lockResolver;
if (navigator && navigator.locks && navigator.locks.request) {
const promise = new Promise((res) => {
lockResolver = res;
});
navigator.locks.request('unique_lock_name', { mode: "shared" }, () => {
return promise;
});
}
Per l'esempio di codice precedente:
- I blocchi Web sono sperimentali. Il controllo condizionale conferma che il browser supporta i blocchi Web.
- Il sistema di risoluzione delle promesse (
lockResolver
) viene archiviato in modo che il blocco possa essere rilasciato quando è accettabile che la scheda sia in sospensione. - Quando si chiude la connessione, il blocco viene rilasciato chiamando
lockResolver()
. Quando il blocco viene rilasciato, la scheda è autorizzata a dormire.