ASP.NET Core SignalR client JavaScript

La libreria client JavaScript di ASP.NET Core SignalR consente agli sviluppatori di chiamare il codice dell'hub sul lato SignalR server. Questo articolo descrive come usare le API per connettersi a un SignalR hub e chiamare l'hub JavaScript e i metodi client.

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 . Esistono diversi modi per installare la libreria client:

  • Eseguite i comandi npm in Visual Studio Gestione pacchetti Console.
  • Eseguire i comandi npm nel terminale integrato in Visual Studio Code.
  • Fare riferimento a una copia della libreria client ospitata in una rete CDN (Content Delivery Network).
  • Usare LibMan e installare file di libreria client specifici da una libreria client ospitata dalla rete CDN.

Eseguire l'installazione con npm

Eseguire i comandi npm seguenti nella finestra Gestione pacchetti Console:

npm init -y
npm install @microsoft/signalr

npm installa il contenuto del pacchetto nella cartella node_modules\@microsoft\signalr\dist\browser.

Completare la configurazione:

  1. Creare la cartella wwwroot/lib/signalr.

  2. Copiare il filesignalr.js nella cartella wwwroot/lib/signalr .

È ora possibile fare riferimento al client JavaScript installato SignalR nell'elemento <script> . Ad esempio:

<script src="~/lib/signalr/signalr.js"></script>

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

L'esempio di codice specifica la versione 6.0.1. Per ottenere la versione più recente della libreria client, scegliere una delle reti CDN seguenti:

Eseguire l'installazione con LibMan

Un altro approccio consiste nell'usare LibMan e installare solo file di libreria client specifici dalla libreria client ospitata dalla rete CDN. Ad esempio, è possibile aggiungere al progetto solo il file JavaScript minimizzato.

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

Per le richieste tra domini, il codice client deve usare un URL assoluto anziché un URL relativo. Quando si usano 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();

Il UseCors metodo deve essere chiamato prima di chiamare il MapHub metodo .

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 a invoke corrispondono agli argomenti user e message del 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 Azure SignalR in modalità predefinita.

Il invoke metodo restituisce un oggetto Promise JavaScript. 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. Per gestire questi casi, utilizzare async e await oppure i metodi Promise e then dell'oggetto catch.

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 viene inviato al server. Se si verifica un errore durante l'invio del messaggio, l'oggetto Promise viene rifiutato con il messaggio di errore. Per gestire questi casi, utilizzare async e await oppure i metodi Promise e then dell'oggetto catch.

L'uso di senddoesn't non aspetta che il server riceva il messaggio, quindi non è possibile restituire dal server dati o errori.

Chiamare i metodi client dall'hub

Per ricevere messaggi dall'hub, definire un metodo usando il metodo on di HubConnection. Il on metodo accetta:

  • 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. Questo approccio garantisce che i gestori vengano 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);
}

Configura il tracciamento dei log 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:

Livello di registrazione Dati registrati Descrzione
signalR.LogLevel.Error Messaggi di errore Registra solo i Error messaggi.
signalR.LogLevel.Warning Messaggi di avviso relativi a potenziali errori Registra i messaggi Warning e Error.
signalR.LogLevel.Information Messaggi di stato senza errori Registra i messaggi Information, Warning e 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();

Riconnetti i client

Per riconnettersi con i client, è possibile configurare la riconnessione automatica o configurare manualmente la connessione.

Riconnettersi automaticamente

Il client JavaScript per SignalR può essere configurato per riconnettersi automaticamente utilizzando il metodo WithAutomaticReconnect su 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. Dopo quattro tentativi non riusciti, smette di tentare di riconnettersi.

Prima di avviare qualsiasi riconnessione, HubConnection:

  • Passa allo stato HubConnectionState.Reconnecting e ne attiva i onreconnecting callback.
  • Non esegue la transizione allo stato Disconnected né attiva i propri callback onclose come farebbe un HubConnection senza riconnessione automatica configurata.

L'approccio di riconnessione offre l'opportunità di avvisare gli utenti che la connessione viene 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 torna allo stato Connected e attiva i relativi callback onreconnected. Questo approccio offre l'opportunità di informare gli utenti che la connessione è stata ristabilita.

Poiché la connessione appare al server come del tutto nuova, un nuovo connectionId viene fornito alla callback onreconnected.

Il parametro onreconnected del callback connectionId è undefined se HubConnection è configurato per saltare la 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 configura HubConnection in modo che riprovi in caso di fallimenti di avvio iniziale, quindi i fallimenti 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 passa allo stato Disconnected e attiva i callback onclose. Questo approccio offre un'opportunità per informare gli utenti che la connessione viene persa definitivamente e di 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 (0) secondi per tentare di riconnettersi.

  • Se il primo tentativo di riconnessione ha esito negativo, il secondo tentativo di riconnessione viene avviato immediatamente anziché attendere 2 secondi, come definito nella configurazione predefinita.

  • Se il secondo tentativo di riconnessione non riesce, il terzo tentativo di riconnessione viene avviato in 10 secondi, ovvero lo stesso comportamento definito nella configurazione predefinita.

  • L'intervallo di riconnessione configurato differisce dal comportamento predefinito arrestando dopo il terzo tentativo di riconnessione non riuscito. Nella configurazione predefinita viene eseguito un altro tentativo di riconnessione dopo 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. RetryContext ha tre proprietà: previousRetryCount (tipo number), elapsedMilliseconds (tipo number) e retryReason (tipo Error).

  • Prima del primo tentativo previousRetryCount di riconnessione e elapsedMilliseconds sono entrambi zero (0) ed retryReason è l'errore che ha causato la connessione persa.

  • Dopo ogni tentativo di ripetizione non riuscito, previousRetryCount incrementa di uno, elapsedMilliseconds aggiorna in modo da riflettere la quantità di tempo impiegato per la riconnessione finora in millisecondi ed retryReason è l'errore che ha causato l'esito negativo dell'ultimo tentativo di riconnessione.

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 per riconnettere il client manualmente, come illustrato nella sezione successiva.

Riconnettersi manualmente

Il codice seguente illustra un tipico approccio di riconnessione manuale:

  1. Per avviare la connessione, creare una funzione. In questo caso, la start funzione.

  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 inattiva del browser

Alcuni browser dispongono di una funzione di blocco delle schede o sospensione delle schede che riduce il consumo di risorse del computer per le schede inattive. Tuttavia, questa funzionalità può causare SignalR la chiusura delle connessioni e causare un'esperienza utente indesiderata.

I browser implementano euristica per determinare se una scheda deve essere messa in sospensione, ad esempio:

  • Riproduzione di audio
  • Mantenimento di un blocco web
  • Tenere premuto un IndexedDB blocco
  • Connessione a un dispositivo USB
  • Acquisizione di video o audio
  • In fase di mirroring
  • Cattura di una finestra o di uno schermo

L'euristica può cambiare nel tempo e anche differire 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.

L'esempio di codice seguente illustra 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;
    });
}

Nel codice precedente:

  • Web Locks sono sperimentali. Il controllo condizionale conferma che il browser supporta i blocchi Web.
  • Il sistema di risoluzione delle promesse JavaScript (lockResolver) viene archiviato in modo che il blocco possa essere rilasciato quando è accettabile che la scheda sia in sospensione.
  • Quando la connessione viene chiusa, il blocco viene rilasciato chiamando lockResolver(). Quando il blocco viene rilasciato, la scheda è autorizzata a dormire.

Aggiornare l'autenticazione senza riconnettersi

In .NET 11 e versioni successive, un client può sostituire le credenziali in una connessione aperta invece di consentire la chiusura della connessione alla scadenza del token di accesso. Il server deve abilitare l'aggiornamento dell'autenticazione per l'hub. Chiamare withAuthenticationRefresh per pianificare un aggiornamento automatico prima della scadenza che il server segnala:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub", { accessTokenFactory: () => getAccessToken() })
    .withAuthenticationRefresh()
    .build();

Ogni aggiornamento chiama accessTokenFactory di nuovo per ottenere un nuovo token. Per aggiornare immediatamente, ad esempio dopo che l'app ottiene attestazioni aggiornate, chiamare connection.refreshAuthentication().

Per le opzioni di aggiornamento, i gestori di esito positivo e negativo e la configurazione del server che abilita la funzionalità, vedere SignalR Autenticazione e autorizzazione.

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\signalr\dist\browser. Creare una nuova cartella denominata signalr nella cartella wwwroot\lib. Copiare il file signalr.js 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 maggiori dettagli su questo approccio, consulta Aggiungere la libreria SignalR 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 a invoke corrispondono agli argomenti user e message del 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 Azure SignalR in modalità Default.

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 await oppure i metodi Promise e then di 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 await oppure i metodi Promise e then di catch per gestire questi casi.

Nota

Usare send non aspetta che il server abbia 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 catch con async e await oppure il metodo Promise di catch per gestire gli errori 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);
}

Configura il tracciamento dei log 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 messaggi Warning e Error.
  • signalR.LogLevel.Information: messaggi di stato senza errori. Registra i messaggi Information, Warning e 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();

Riconnetti i client

Riconnettersi automaticamente

Il client JavaScript per SignalR può essere configurato per riconnettersi automaticamente usando il metodo withAutomaticReconnect su HubConnectionBuilder. Per impostazione predefinita, non si ricollegherà 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, HubConnection passerà allo stato HubConnectionState.Reconnecting ed eseguirà i callback onreconnecting invece di passare allo stato Disconnected e attivare i callback onclose, come farebbe 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 con successo entro i primi quattro tentativi, HubConnection tornerà allo stato Connected e attiverà i callback onreconnected. In questo modo è possibile informare gli utenti che la connessione è stata ristabilita.

Poiché la connessione appare come del tutto nuova al server, un nuovo connectionId verrà fornito alla funzione di callback onreconnected.

Avviso

Il parametro di onreconnected callback connectionId sarà indefinito se HubConnection è stato configurato per ignorare la 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à HubConnection per riprovare in caso di errori di avvio iniziali, quindi gli errori di avvio dovranno 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 riesce a riconnettersi con successo entro i primi quattro tentativi, il HubConnection passerà allo stato Disconnected e 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. RetryContext ha tre proprietà: previousRetryCount, elapsedMilliseconds e retryReason, che sono rispettivamente un number, un number e un 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 inattiva 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
  • Mantenimento di un blocco web
  • Tenere premuto un IndexedDB blocco
  • Connessione a un dispositivo USB
  • Acquisizione di video o audio
  • In fase di mirroring
  • Cattura di una finestra o di uno schermo

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:

  • Web Locks 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.