SignalR ASP.NET Core client JavaScript
Di Rachel Appel
La libreria client JavaScript ASP.NET Core SignalR consente agli sviluppatori di chiamare il codice dell'hub lato SignalR server.
Installare il SignalR pacchetto client
La SignalR libreria client JavaScript viene recapitata come pacchetto npm . Le sezioni seguenti illustrano diversi modi per installare la libreria client.
Installare con npm
Eseguire i comandi seguenti dalla console di Gestione pacchetti:
npm init -y
npm install @microsoft/signalr
npm installa il contenuto del pacchetto nella cartella node_modules\@microsoft\signalr\dist\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 una 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:
Installare 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 minified 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 è senza 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, ci sono occasioni in cui è necessaria una connessione a un altro dominio.
Quando si effettuano richieste di dominio incrociate, 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 i dati sensibili da un altro sito, le connessioni tra origini vengono disabilitate per impostazione predefinita. Per consentire una richiesta tra 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.
- Tutti gli argomenti definiti nel metodo hub.
Nel codice evidenziato seguente il nome del metodo nell'hub è SendMessage
. I secondi e i terzi argomenti passati al invoke
mapping agli argomenti e message
al metodo user
hub:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
I metodi dell'hub chiamante da un client sono supportati solo quando si usa il servizio di Azure SignalR in modalità predefinita . Per altre informazioni, vedere Domande frequenti (repository GitHub azure-signalr).
Il invoke
metodo restituisce un codice JavaScript Promise
. L'oggetto Promise
viene risolto con il valore restituito (se presente) quando il metodo nel server restituisce. Se il metodo nel server genera un errore, viene Promise
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 dell'oggetto HubConnection
. A differenza del metodo, il invoke
send
metodo non attende una risposta dal server. Il send
metodo restituisce un codice JavaScript Promise
. Viene Promise
risolto quando il messaggio è stato inviato al server. Se si verifica un errore durante l'invio del messaggio, viene Promise
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 fino a quando 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 dell'hub passano 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 viene connection.on
eseguito quando il codice 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 corrispondendo al nome del metodo e agli argomenti definiti in SendAsync
e connection.on
.
Una procedura consigliata consiste nel chiamare il metodo start in 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 può connettersi o inviare un messaggio:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Configurare la traccia del log lato client passando un logger e un tipo di evento per registrare quando viene eseguita 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. RegistraError
solo i messaggi.signalR.LogLevel.Warning
: messaggi di avviso relativi a potenziali errori. RegistraWarning
eError
messaggi.signalR.LogLevel.Information
: messaggi di stato senza errori. RegistraInformation
,Warning
eError
i messaggi.signalR.LogLevel.Trace
: messaggi di traccia. Registra tutto, 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 SignalR può essere configurato per riconnettersi automaticamente usando il metodo WithAutomaticReconnect in HubConnectionBuilder. Non si riconnetterà automaticamente per impostazione predefinita.
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 a riconnettersi. Dopo quattro tentativi non riusciti, si arresta il tentativo di riconnessione.
Prima di avviare eventuali tentativi di riconnessione, l'oggetto HubConnection
:
- Passa allo
HubConnectionState.Reconnecting
stato e attiva ionreconnecting
callback. - Non passa allo
Disconnected
stato e attiva ionclose
callback come unHubConnection
senza riconnessione automatica configurata.
L'approccio di riconnessione offre l'opportunità di:
- 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, HubConnection
torna allo Connected
stato e genera i onreconnected
callback. In questo modo è possibile informare gli utenti che la connessione è stata ristabilita.
Poiché la connessione ha un aspetto completamente nuovo per il server, viene fornito un nuovo connectionId
al onreconnected
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 attiva i callback di chiusura . 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 per HubConnection
avviare il tentativo di riconnessione immediatamente dopo la perdita della connessione. La configurazione predefinita attende anche zero secondi per tentare la riconnessione.
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 ha esito negativo, il terzo tentativo di riconnessione inizia in 10 secondi, che corrisponde alla configurazione predefinita.
L'intervallo di riconnessione configurato è diverso 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
. l'oggetto RetryContext
ha tre proprietà: elapsedMilliseconds
previousRetryCount
e retryReason
che sono rispettivamente , number
e number
.Error
Prima del primo tentativo di riconnessione, entrambi previousRetryCount
e elapsedMilliseconds
saranno zero e sarà l'errore retryReason
che ha causato la perdita della connessione. Dopo ogni tentativo 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 retryReason
sarà 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 successivo tentativo di riconnessione 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, il codice può essere scritto che riconnette manualmente il client, 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 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 l'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 resolver promise,
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 (come scaricare)
- Informazioni di riferimento sulle API JavaScript
- Esercitazione su JavaScript
- Esercitazione su WebPack e TypeScript
- Hub
- Client .NET
- Pubblicazione 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 ASP.NET Core SignalR consente agli sviluppatori di chiamare il codice dell'hub sul lato server.
Visualizzare o scaricare il codice di esempio (come scaricare)
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\signalr\dist\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 una 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, ci sono occasioni in cui è necessaria una connessione a un altro dominio.
Importante
Il codice client deve usare un URL assoluto anziché un URL relativo. Cambiare .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 (repository GitHub azure-signalr).
Il invoke
metodo restituisce un oggetto JavaScript Promise
. L'oggetto Promise
viene risolto con il valore restituito (se presente) quando il metodo nel server restituisce . Se il metodo nel server genera un errore, viene Promise
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, viene Promise
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
di non attende fino a quando 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 dell'hub passano 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. RegistraWarning
i 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. Non si riconnette automaticamente per impostazione predefinita.
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 eventuali tentativi di riconnessione, l'oggetto HubConnection
HubConnectionState.Reconnecting
passerà allo stato e attiverà onreconnecting
i callback anziché passare allo Disconnected
stato e attivando onclose
i callback come un HubConnection
senza riconnessione automatica configurata. In questo modo è possibile avvisare gli utenti che la connessione è stata persa e 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
eseguirà la Connected
transizione allo stato e attiverà onreconnected
i relativi callback. In questo modo è possibile informare gli utenti che la connessione è stata ristabilita.
Poiché la connessione ha un aspetto completamente nuovo per il server, verrà fornito un nuovo connectionId
al onreconnected
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, passerà HubConnection
allo Disconnected
stato e attiverà i callback di chiusura . 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 per HubConnection
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 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
. l'oggetto RetryContext
ha tre proprietà: elapsedMilliseconds
previousRetryCount
e retryReason
che sono rispettivamente , number
e number
.Error
Prima del primo tentativo di riconnessione, entrambi previousRetryCount
e elapsedMilliseconds
saranno zero e sarà l'errore retryReason
che ha causato la perdita della connessione. Dopo ogni tentativo 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 retryReason
sarà 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 successivo tentativo di riconnessione 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à manualmente il client, 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 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 l'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 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.