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 Promisethen 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 Promisethen 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 i Error messaggi.
  • signalR.LogLevel.Warning: messaggi di avviso relativi a potenziali errori. Registra i Warningmessaggi e Error .
  • signalR.LogLevel.Information: messaggi di stato senza errori. Registra Informationi messaggi , Warninge Error .
  • 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 i onreconnecting callback.
  • Non esegue la transizione allo Disconnected stato e attiva i onclose callback come un HubConnection 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à: previousRetryCountelapsedMilliseconds e retryReason che sono rispettivamente , numbere 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:

  1. Viene creata una funzione (in questo caso, la start funzione ) per avviare la connessione.
  2. Chiamare la start funzione nel gestore eventi della onclose 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

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 Promisethen 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 Promisethen 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 Promisemetodo 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 i Error messaggi.
  • signalR.LogLevel.Warning: messaggi di avviso relativi a potenziali errori. Registra i Warningmessaggi e Error .
  • signalR.LogLevel.Information: messaggi di stato senza errori. Registra Informationi messaggi , Warninge Error .
  • 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à: previousRetryCountelapsedMilliseconds e retryReason che sono rispettivamente , numbere 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:

  1. Viene creata una funzione (in questo caso, la start funzione ) per avviare la connessione.
  2. Chiamare la start funzione nel gestore eventi della onclose 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.

Risorse aggiuntive