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 Promisethen metodi e awaitcatch 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 invokesend 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 Promisethen metodi e awaitcatch per gestire questi casi.

L'uso sendnon 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. Registra Error solo i messaggi.
  • signalR.LogLevel.Warning: messaggi di avviso relativi a potenziali errori. Registra Warninge Error messaggi.
  • signalR.LogLevel.Information: messaggi di stato senza errori. Registra Information, Warninge Error 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 i onreconnecting callback.
  • Non passa allo Disconnected stato e attiva i onclose callback come un HubConnection 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à: elapsedMillisecondspreviousRetryCounte retryReason che sono rispettivamente , numbere 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:

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

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 Promisethen metodi e awaitcatch 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 Promisethen metodi e awaitcatch 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 awaitcatch 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 Warningi messaggi 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. 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 HubConnectionHubConnectionState.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à: elapsedMillisecondspreviousRetryCounte retryReason che sono rispettivamente , numbere 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:

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

Risorse aggiuntive