Client JavaScript SignalR pour ASP.NET Core

La bibliothèque client JavaScript SignalR ASP.NET Core permet aux développeurs d'appeler le code du concentrateur SignalR côté serveur. Cet article explique comment utiliser les API pour se connecter à un SignalR hub et appeler le hub JavaScript et les méthodes clientes.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Installez le SignalR paquet client

La bibliothèque cliente JavaScript SignalR est livrée sous la forme d'un package npm. Il existe plusieurs façons d’installer la bibliothèque cliente :

  • Exécutez des commandes npm dans Visual Studio Gestionnaire de package Console.
  • Exécutez des commandes npm dans le terminal intégré dans Visual Studio Code.
  • Référencez une copie de la bibliothèque cliente hébergée sur un réseau de distribution de contenu (CDN).
  • Utilisez LibMan et installez des fichiers de bibliothèque client spécifiques à partir d’une bibliothèque cliente hébergée par CDN.

Installer avec npm

Exécutez les commandes npm suivantes dans la fenêtre Gestionnaire de package Console :

npm init -y
npm install @microsoft/signalr

npm installe le contenu du package dans le dossier node_modules\@microsoft\signalr\dist\browser.

Terminez la configuration :

  1. Créez le dossier wwwroot/lib/signalr.

  2. Copiez le fichier signalr.js dans le dossier wwwroot/lib/signalr .

Vous pouvez maintenant référencer le client JavaScript installé SignalR dans l’élément <script> . Par exemple:

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

Utiliser un CDN

Pour utiliser la bibliothèque cliente sans le prérequis npm, référencez une copie hébergée sur CDN de la bibliothèque cliente. Par exemple:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>

L’exemple de code spécifie la version 6.0.1. Pour obtenir la dernière version de la bibliothèque cliente, choisissez l’un des CDN suivants :

Installer avec LibMan

Une autre approche consiste à utiliser LibMan et à installer uniquement des fichiers de bibliothèque client spécifiques à partir de la bibliothèque cliente hébergée par CDN. Par exemple, vous ne pouvez ajouter que le fichier JavaScript minifié au projet.

Pour plus d’informations sur cette approche, consultez Ajouter la SignalR bibliothèque cliente.

Se connecter à un hub

Le code suivant crée et démarre une connexion. Le nom du hub n’est pas sensible à la casse :

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();

Connexions d'origine croisée (CORS)

Généralement, les navigateurs chargent les connexions à partir du même domaine que la page demandée. Cependant, il arrive parfois qu'une connexion à un autre domaine soit requise.

Pour les demandes inter-domaines, le code client doit utiliser une URL absolue plutôt qu’une URL relative. Lorsque vous utilisez des requêtes inter-domaines, passez .withUrl("/chathub") à .withUrl("https://{App domain name}/chathub").

Pour empêcher un site malveillant de lire les données sensibles d'un autre site, les connexions cross-origin sont désactivées par défaut. Pour autoriser une requête cross-origin, activez 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();

La UseCors méthode doit être appelée avant d’appeler la MapHub méthode.

Appeler les méthodes du concentrateur depuis le client

Les clients JavaScript appellent des méthodes publiques sur les hubs via la méthode d'invocation de HubConnection. La méthode invoke accepte :

  • Le nom de la méthode hub.
  • Tous les arguments définis dans la méthode hub.

Dans le code suivant mis en évidence, le nom de la méthode du hub est SendMessage. Les deuxième et troisième arguments transmis à invoke correspondent aux arguments user et message de la méthode du concentrateur :

try {
    await connection.invoke("SendMessage", user, message);
} catch (err) {
    console.error(err);
}

L’appel de méthodes hub à partir d’un client est pris en charge uniquement lors de l’utilisation du mode Azure SignalR Service par défaut. Pour plus d’informations, consultez Foire aux questions (dépôt GitHub azure-signalr).

La invoke méthode retourne un objet JavaScript Promise . L’objet Promise est résolu avec la valeur de retour (le cas échéant) lorsque la méthode sur le serveur retourne. Si la méthode sur le serveur lève une erreur, l’objet Promise est rejeté avec le message d’erreur. Pour gérer ces cas, utilisez async et await, ou les méthodes then et catch de l’objet Promise.

Les clients JavaScript peuvent également appeler des méthodes publiques sur les hubs via la méthode envoyer du HubConnection. Contrairement à la méthode invoke, la méthode send n'attend pas de réponse du serveur. La send méthode retourne un objet JavaScript Promise . L’objet Promise est résolu lorsque le message est envoyé au serveur. Si une erreur se produit lors de l’envoi du message, l’objet Promise est rejeté avec le message d’erreur. Pour gérer ces cas, utilisez async et await ou les méthodes then et catch de l’objet Promise.

L’utilisation send n’attend pas que le serveur reçoive le message. Il n’est donc pas possible de retourner des données ou des erreurs du serveur.

Appeler des méthodes client depuis le hub

Pour recevoir des messages provenant du hub, définissez une méthode en utilisant la méthode on de HubConnection. La méthode on accepte :

  • Nom de la méthode client JavaScript.
  • Arguments que le hub transmet à la méthode.

Dans l'exemple suivant, le nom de la méthode est ReceiveMessage. Les noms des arguments sont user et message :

connection.on("ReceiveMessage", (user, message) => {
    const li = document.createElement("li");
    li.textContent = `${user}: ${message}`;
    document.getElementById("messageList").appendChild(li);
});

Le code précédent dans connection.on s’exécute lorsque le code côté serveur l’appelle en utilisant la méthode SendAsync :

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 détermine la méthode cliente à appeler en faisant correspondre le nom de la méthode et les arguments définis dans SendAsync et connection.on.

Une bonne pratique consiste à appeler la méthode start sur le HubConnection après on. Cette approche garantit que les gestionnaires sont inscrits avant la réception des messages.

Gestion et journalisation des erreurs

Utilisez console.error pour envoyer des erreurs à la console du navigateur lorsque le client ne peut pas se connecter ou envoyer un message :

try {
    await connection.invoke("SendMessage", user, message);
} catch (err) {
    console.error(err);
}

Configurez le suivi des journaux côté client en transmettant un journal et un type d'événement à consigner lorsque la connexion est établie. Les messages sont consignés avec le niveau de journalisation spécifié et supérieur. Les niveaux de journalisation disponibles sont les suivants :

Niveau de journalisation Données journalisées Description
signalR.LogLevel.Error Messages d’erreur Consigne uniquement les messages Error.
signalR.LogLevel.Warning Messages d’avertissement concernant les erreurs potentielles Consigne les messages Warning et Error.
signalR.LogLevel.Information Messages d’état sans erreurs Consigne les messages Information, Warning et Error.
signalR.LogLevel.Trace Messages de suivi Enregistre tout, y compris les données transportées entre le hub et le client.

Utilisez la méthode configureLogging sur HubConnectionBuilder pour configurer le niveau de journalisation. Les messages sont consignés dans la console du navigateur :

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .configureLogging(signalR.LogLevel.Information)
    .build();

Reconnectez les clients

Pour vous reconnecter avec les clients, vous pouvez configurer la reconnexion automatique ou configurer la connexion manuellement.

Reconnecter automatiquement

Le client JavaScript pour SignalR peut être configuré pour se reconnecter automatiquement à l’aide de la méthode WithAutomaticReconnect sur HubConnectionBuilder. Elle ne se reconnecte pas automatiquement par défaut.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect()
    .build();

Sans aucun paramètre, WithAutomaticReconnect configure le client pour qu'il attende respectivement 0, 2, 10 et 30 secondes avant d'essayer chaque tentative de reconnexion. Après quatre tentatives infructueuses, il arrête d'essayer de se reconnecter.

Avant d’entamer toute reconnexion, le HubConnection :

  • Passe à l’état HubConnectionState.Reconnecting et déclenche ses fonctions de rappel onreconnecting.
  • Ne passe pas à l’état Disconnected ni ne déclenche ses fonctions de rappel onclose, comme le ferait un HubConnection sans reconnexion automatique configurée.

L’approche de reconnexion permet d’avertir les utilisateurs que la connexion est perdue et de désactiver les éléments de l’interface utilisateur.

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);
});

Si le client se reconnecte avec succès au cours de ses quatre premières tentatives, le HubConnection repasse à l’état Connected et déclenche ses rappels onreconnected. Cette approche offre la possibilité d’informer les utilisateurs que la connexion est maintenant rétablie.

Comme la connexion semble totalement nouvelle du point de vue du serveur, une nouvelle connectionId est fournie à la fonction de rappel onreconnected.

Le onreconnected paramètre du connectionId rappel n’est pas défini si HubConnection est configuré pour ignorer la négociation.

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 ne configure pas le HubConnection pour réessayer en cas d’échec du démarrage initial ; les échecs de démarrage doivent donc être gérés manuellement :

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);
    }
};

Si le client ne parvient pas à se reconnecter au cours de ses quatre premières tentatives, le HubConnection passe à l’état Disconnected et déclenche ses rappels onclose. Cette approche offre la possibilité d’informer les utilisateurs que la connexion est définitivement perdue et d’essayer d’actualiser la page :

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);
});

Pour configurer un nombre personnalisé de tentatives de reconnexion avant de déconnecter ou de modifier le minutage de reconnexion, withAutomaticReconnect accepte un tableau de nombres représentant le délai en millisecondes à attendre avant de démarrer chaque tentative de reconnexion.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect([0, 0, 10000])
    .build();

    // .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior

L'exemple précédent configure le HubConnection pour qu'il commence à tenter des reconnexions immédiatement après la perte de la connexion. La configuration par défaut attend également zéro (0) secondes pour tenter de se reconnecter.

  • Si la première tentative de reconnexion échoue, la deuxième tentative de reconnexion démarre également immédiatement au lieu d’attendre 2 secondes, comme défini dans la configuration par défaut.

  • Si la deuxième tentative de reconnexion échoue, la troisième tentative de reconnexion démarre en 10 secondes, ce qui est le même comportement défini dans la configuration par défaut.

  • Le minutage de reconnexion configuré diffère du comportement par défaut en s’arrêtant après le troisième échec de la tentative de reconnexion. Dans la configuration par défaut, une nouvelle tentative de reconnexion est effectuée après 30 secondes.

Pour plus de contrôle sur la synchronisation et le nombre de tentatives de reconnexion automatique, withAutomaticReconnect accepte un objet implémentant l'interface IRetryPolicy, qui a une seule méthode nommée nextRetryDelayInMilliseconds. nextRetryDelayInMilliseconds prend un seul argument de type RetryContext. L’objet RetryContext a trois propriétés : previousRetryCount (type number), elapsedMilliseconds (type number) et retryReason (type Error).

  • Avant la première tentative de reconnexion, previousRetryCount et elapsedMilliseconds sont à la fois zéro (0), et retryReason est l’erreur qui a provoqué la perte de connexion.

  • Après chaque tentative de reconnexion ayant échoué, previousRetryCount est incrémenté de un, elapsedMilliseconds est mis à jour pour refléter le temps passé à se reconnecter jusque-là, en millisecondes, et retryReason est l’erreur qui a provoqué l’échec de la dernière tentative de reconnexion.

nextRetryDelayInMilliseconds doit renvoyer soit un nombre représentant le nombre de millisecondes à attendre avant la prochaine tentative de reconnexion, ou null si le HubConnection doit arrêter de se reconnecter.

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();

Vous pouvez également écrire du code pour reconnecter le client manuellement, comme illustré dans la section suivante.

Reconnecter manuellement

Le code suivant illustre une approche typique de reconnexion manuelle :

  1. Pour démarrer la connexion, créez une fonction. Dans ce cas, la fonction start.

  2. Appelez la fonction start dans le gestionnaire d'événements onclose de la connexion.

async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
};

connection.onclose(async () => {
    await start();
});

Les implémentations de production utilisent généralement un recul exponentiel ou réessayent un nombre de fois spécifié.

Onglet de veille du navigateur

Certains navigateurs disposent d’une fonctionnalité de gel des onglets ou de mise en veille des onglets, permettant de réduire l’utilisation des ressources de l’ordinateur pour les onglets inactifs. Toutefois, cette fonctionnalité peut entraîner la SignalR fermeture des connexions et provoquer une expérience utilisateur indésirable.

Les navigateurs implémentent des heuristiques pour déterminer si un onglet doit être mis en veille, par exemple :

  • Lecture de l’audio
  • Maintenir un verrou Web
  • Maintenir un verrou IndexedDB
  • Être connecté à un périphérique USB
  • Capture vidéo ou audio
  • Mise en miroir
  • Capturer une fenêtre ou un affichage

Les heuristiques peuvent changer au fil du temps et diffèrent également entre les navigateurs. Consultez la matrice de support et déterminez quelle méthode fonctionne le mieux pour vos scénarios.

Pour éviter de mettre une application en veille, l'application doit déclencher l'une des heuristiques utilisées par le navigateur.

L’exemple de code suivant montre comment utiliser un verrou web pour garder un onglet éveillé et éviter une fermeture de connexion inattendue :

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;
    });
}

Dans le code précédent :

  • Les verrous Web sont expérimentaux. La vérification conditionnelle confirme que le navigateur prend en charge les verrous web.
  • Le résolveur de promesse JavaScript (lockResolver) est conservé afin que le verrou puisse être libéré lorsqu’il est acceptable que l’onglet se mette en veille.
  • Lorsque la connexion se ferme, le verrou est libéré en appelant lockResolver(). Lorsque le verrouillage est désactivé, l’onglet peut être mis en veille.

Par Rachel Appel

La bibliothèque client JavaScript SignalR ASP.NET Core permet aux développeurs d'appeler le code du concentrateur côté serveur.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Installez le SignalR paquet client

La bibliothèque cliente JavaScript SignalR est livrée sous la forme d'un package npm. Les sections suivantes décrivent les différentes manières d'installer la bibliothèque cliente.

Installer avec npm

Pour Visual Studio, exécutez les commandes suivantes à partir de la console du gestionnaire de packages dans le dossier racine. Pour Visual Studio Code, exécutez les commandes suivantes à partir du terminal intégré.

npm init -y
npm install @microsoft/signalr

npm installe le contenu du package dans le dossier node_modules\@microsoft\signalr\dist\browser. Créez un dossier nommé signaleur sous le dossier wwwroot\lib. Copiez le fichier signalr.js dans le dossier wwwroot\lib\signalr.

Référencez le client JavaScript SignalR dans l'élément <script>. Par exemple:

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

Utiliser un réseau de diffusion de contenu (CDN)

Pour utiliser la bibliothèque cliente sans le prérequis npm, référencez une copie hébergée sur CDN de la bibliothèque cliente. Par exemple:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.js"></script>

La bibliothèque cliente est disponible sur les CDN suivants :

Installer avec LibMan

LibMan peut être utilisé pour installer des fichiers de bibliothèque client spécifiques à partir de la bibliothèque client hébergée par CDN. Par exemple, ajoutez uniquement le fichier JavaScript minifié au projet. Pour plus de détails sur cette approche, consultez Ajouter la bibliothèque SignalR cliente.

Se connecter à un hub

Le code suivant crée et démarre une connexion. Le nom du hub est insensible à la casse :

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();

Connexions entre origines

Généralement, les navigateurs chargent les connexions à partir du même domaine que la page demandée. Cependant, il arrive parfois qu'une connexion à un autre domaine soit requise.

Important

Le code client doit utiliser une URL absolue au lieu d'une URL relative. Remplacez .withUrl("/chathub") par .withUrl("https://myappurl/chathub").

Pour empêcher un site malveillant de lire les données sensibles d'un autre site, les connexions cross-origin sont désactivées par défaut. Pour autoriser une requête cross-origin, activez-la dans la classe Startup :

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");
            });
        }
    }
}

Appeler les méthodes du concentrateur depuis le client

Les clients JavaScript appellent des méthodes publiques sur les hubs via la méthode d'invocation de HubConnection. La méthode invoke accepte :

  • Le nom de la méthode hub.
  • Tous les arguments définis dans la méthode hub.

Dans l'exemple suivant, le nom de la méthode sur le concentrateur est SendMessage. Les deuxième et troisième arguments transmis à invoke correspondent aux arguments user et message de la méthode du concentrateur :

try {
    await connection.invoke("SendMessage", user, message);
} catch (err) {
    console.error(err);
}

Note

L'appel de méthodes de concentrateur à partir d'un client est uniquement pris en charge lors de l'utilisation du service SignalR Azure en mode par défaut. Pour plus d’informations, consultez Foire aux questions (dépôt GitHub azure-signalr).

La méthode invoke retourne un élément JavaScript Promise. Le Promise est résolu avec la valeur de retour (le cas échéant) lorsque la méthode sur le serveur revient. Si la méthode sur le serveur génère une erreur, le Promise est rejeté avec le message d'erreur. Utilisez async et await ou les méthodes Promise’s then et catch pour gérer ces cas.

Les clients JavaScript peuvent également appeler des méthodes publiques sur les hubs via la méthode envoyer du HubConnection. Contrairement à la méthode invoke, la méthode send n'attend pas de réponse du serveur. La méthode send retourne un élément JavaScript Promise. Le Promise est résolu lorsque le message a été envoyé au serveur. S'il y a une erreur lors de l'envoi du message, le Promise est rejeté avec le message d'erreur. Utilisez async et await ou les méthodes Promise’s then et catch pour gérer ces cas.

Note

L'utilisation send n'attend pas que le serveur ait reçu le message. Par conséquent, il n'est pas possible de renvoyer des données ou des erreurs du serveur.

Appeler des méthodes client depuis le hub

Pour recevoir des messages du hub, définissez une méthode à l'aide de la méthode on du HubConnection.

  • Nom de la méthode client JavaScript.
  • Arguments que le hub transmet à la méthode.

Dans l'exemple suivant, le nom de la méthode est ReceiveMessage. Les noms des arguments sont user et message :

connection.on("ReceiveMessage", (user, message) => {
    const li = document.createElement("li");
    li.textContent = `${user}: ${message}`;
    document.getElementById("messageList").appendChild(li);
});

Le code précédent dans connection.on s'exécute lorsque le code côté serveur l'appelle à l'aide de la méthode SendAsync :

public async Task SendMessage(string user, string message)
{
    await Clients.All.SendAsync("ReceiveMessage", user, message);
}

SignalR détermine la méthode cliente à appeler en faisant correspondre le nom de la méthode et les arguments définis dans SendAsync et connection.on.

Note

Comme bonne pratique, appelez la méthode start sur le HubConnection après on. Cela garantit que vos gestionnaires sont enregistrés avant la réception de tout message.

Gestion et journalisation des erreurs

Utilisez try et catch avec async et await ou la méthode Promise de catch pour gérer les erreurs côté client. Utilisez console.error pour afficher les erreurs dans la console du navigateur :

try {
    await connection.invoke("SendMessage", user, message);
} catch (err) {
    console.error(err);
}

Configurez le suivi des journaux côté client en transmettant un journal et un type d'événement à consigner lorsque la connexion est établie. Les messages sont consignés avec le niveau de journalisation spécifié et supérieur. Les niveaux de journalisation disponibles sont les suivants :

  • signalR.LogLevel.Error: Messages d'erreur. Consigne uniquement les messages Error.
  • signalR.LogLevel.Warning: Messages d'avertissement sur les erreurs potentielles. Consigne les messages Warning et Error.
  • signalR.LogLevel.Information: Messages d'état sans erreur. Consigne les messages Information, Warning et Error.
  • signalR.LogLevel.Trace : Messages de trace. Enregistre tout, y compris les données transportées entre le hub et le client.

Utilisez la méthode configureLogging sur HubConnectionBuilder pour configurer le niveau de journalisation. Les messages sont consignés dans la console du navigateur :

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .configureLogging(signalR.LogLevel.Information)
    .build();

Reconnectez les clients

Reconnecter automatiquement

Le client JavaScript pour SignalR peut être configuré pour se reconnecter automatiquement à l'aide de la méthode withAutomaticReconnect sur HubConnectionBuilder. Il ne se reconnectera pas automatiquement par défaut.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect()
    .build();

Sans aucun paramètre, withAutomaticReconnect() configure le client pour attendre 0, 2, 10 et 30 secondes respectivement avant d'essayer chaque tentative de reconnexion, s'arrêtant après quatre tentatives infructueuses.

Avant de commencer toute tentative de reconnexion, le HubConnection passera à l'état HubConnectionState.Reconnecting et déclenchera ses rappels onreconnecting au lieu de passer à l'état Disconnected et de déclencher ses rappels onclose comme un HubConnection sans reconnexion automatique configurée. Cela permet d'avertir les utilisateurs que la connexion a été perdue et de désactiver les éléments de l'interface utilisateur.

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);
});

Si le client se reconnecte avec succès au cours de ses quatre premières tentatives, le HubConnection reviendra à l'état Connected et déclenchera ses rappels onreconnected. Cela permet d'informer les utilisateurs que la connexion a été rétablie.

Comme la connexion semble entièrement nouvelle pour le serveur, un nouveau connectionId sera fourni à la fonction de rappel onreconnected.

Warning

Le paramètre onreconnected du rappel connectionId sera indéfini si le HubConnection a été configuré pour ignorer la négociation.

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() ne configurera pas le HubConnection pour réessayer les échecs de démarrage initiaux, donc les échecs de démarrage doivent être gérés manuellement :

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);
    }
};

Si le client ne parvient pas à se reconnecter au cours de ses quatre premières tentatives, le HubConnection passera à l'état Disconnected et déclenchera ses rappels onclose. Cela permet d'informer les utilisateurs que la connexion a été définitivement perdue et de recommander d'actualiser la page :

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);
});

Afin de configurer un nombre personnalisé de tentatives de reconnexion avant de se déconnecter ou de modifier le délai de reconnexion, withAutomaticReconnect accepte un tableau de nombres représentant le délai en millisecondes à attendre avant de démarrer chaque tentative de reconnexion.

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect([0, 0, 10000])
    .build();

    // .withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior

L'exemple précédent configure le HubConnection pour qu'il commence à tenter des reconnexions immédiatement après la perte de la connexion. Ceci est également vrai pour la configuration par défaut.

Si la première tentative de reconnexion échoue, la deuxième tentative de reconnexion démarrera également immédiatement au lieu d'attendre 2 secondes comme dans la configuration par défaut.

Si la deuxième tentative de reconnexion échoue, la troisième tentative de reconnexion démarrera dans 10 secondes, ce qui est à nouveau comme la configuration par défaut.

Le comportement personnalisé s'écarte alors à nouveau du comportement par défaut en s'arrêtant après l'échec de la troisième tentative de reconnexion au lieu d'essayer une autre tentative de reconnexion dans 30 secondes supplémentaires comme il le ferait dans la configuration par défaut.

Si vous souhaitez encore plus de contrôle sur la synchronisation et le nombre de tentatives de reconnexion automatique, withAutomaticReconnect accepte un objet implémentant l'interface IRetryPolicy, qui a une seule méthode nommée nextRetryDelayInMilliseconds.

nextRetryDelayInMilliseconds prend un seul argument de type RetryContext. Le RetryContext a trois propriétés : previousRetryCount, elapsedMilliseconds et retryReason qui sont respectivement un number, un number et un Error. Avant la première tentative de reconnexion, previousRetryCount et elapsedMilliseconds seront à zéro, et le retryReason sera l'erreur qui a causé la perte de la connexion. Après chaque tentative infructueuse, previousRetryCount sera incrémenté de un, elapsedMilliseconds sera mis à jour pour refléter le temps passé à se reconnecter jusqu'à présent en millisecondes, et retryReason sera l'erreur qui a causé l'échec de la dernière tentative de reconnexion.

nextRetryDelayInMilliseconds doit renvoyer soit un nombre représentant le nombre de millisecondes à attendre avant la prochaine tentative de reconnexion, ou null si le HubConnection doit arrêter de se reconnecter.

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();

Vous pouvez également écrire du code qui reconnectera votre client manuellement, comme illustré dans Reconnexion manuelle.

Reconnecter manuellement

Le code suivant illustre une approche typique de reconnexion manuelle :

  1. Une fonction (dans ce cas, la fonction start) est créée pour démarrer la connexion.
  2. Appelez la fonction start dans le gestionnaire d'événements onclose de la connexion.
async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
};

connection.onclose(async () => {
    await start();
});

Les implémentations de production utilisent généralement un recul exponentiel ou réessayent un nombre de fois spécifié.

Onglet de veille du navigateur

Certains navigateurs ont une fonction de gel ou de mise en veille des onglets pour réduire l'utilisation des ressources informatiques pour les onglets inactifs. Cela peut entraîner la fermeture des connexions SignalR et entraîner une expérience utilisateur indésirable. Les navigateurs utilisent des heuristiques pour déterminer si un onglet doit être mis en veille, par exemple :

  • Lecture de l’audio
  • Maintenir un verrou Web
  • Maintenir un verrou IndexedDB
  • Être connecté à un périphérique USB
  • Capture vidéo ou audio
  • Mise en miroir
  • Capturer une fenêtre ou un affichage

Note

Ces heuristiques peuvent changer au fil du temps ou différer entre les navigateurs. Vérifiez votre matrice de support et déterminez quelle méthode fonctionne le mieux pour vos scénarios.

Pour éviter de mettre une application en veille, l'application doit déclencher l'une des heuristiques utilisées par le navigateur.

L'exemple de code suivant montre comment utiliser un verrou Web pour garder un onglet actif et éviter une fermeture de connexion inattendue.

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;
    });
}

Pour l'exemple de code précédent :

  • Les verrous Web sont expérimentaux. La vérification conditionnelle confirme que le navigateur prend en charge Web Locks.
  • La fonction de résolution de la promesse (lockResolver) est conservée afin que le verrou puisse être libéré lorsqu'il est possible de mettre l'onglet en veille.
  • Lors de la fermeture de la connexion, le verrou est libéré en appelant lockResolver(). Lorsque le verrouillage est désactivé, l’onglet peut être mis en veille.

Ressources supplémentaires