Partager via


Client JavaScript SignalR ASP.NET Core

Par Rachel Appel

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

Installer le SignalR package 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

Exécutez les commandes suivantes depuis la console du gestionnaire de packages :

npm init -y
npm install @microsoft/signalr

npm installe le contenu du package dans le dossier node_modules\@microsoft\signalr\dist\browser. Créez le dossier wwwroot/lib/signalr. 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/6.0.1/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 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.

Lors de requêtes inter-domaines, le code client doit utiliser une URL absolue au lieu d'une URL relative. Pour les demandes inter-domaines, remplacez-les .withUrl("/chathub") par .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();

UseCors doit être appelé avant d'appelerMapHub.

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 en surbrillance suivant, le nom de la méthode sur le concentrateur est SendMessage. Les deuxième et troisième arguments passés pour mapper invoke aux arguments user et message de la méthode hub :

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

L'appel de méthodes de concentrateur à partir d'un client est uniquement pris en charge lors de l'utilisation du service Azure en mode SignalRpar défaut. Pour plus d’informations, consultez la section Foire aux questions (référentiel 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.

L’utilisation de 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 concentrateur 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 :

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. Cela garantit que les gestionnaires sont enregistrés avant la réception de tout message.

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 :

  • signalR.LogLevel.Error: Messages d'erreur. Journaux Error uniquement messages.
  • signalR.LogLevel.Warning: Messages d'avertissement sur les erreurs potentielles. Journaux Warning, et Error messages.
  • signalR.LogLevel.Information: Messages d'état sans erreur. Journaux Information, Warninget Error messages.
  • signalR.LogLevel.Trace: Tracer les messages. 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();

Reconnecter les clients

Se 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 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 de commencer toute tentative de reconnexion, le HubConnection:

  • Passe à l'état HubConnectionState.Reconnecting et déclenche ses rappels onreconnecting.
  • Ne passe pas à l'état Disconnected et ne déclenche pas ses rappels onclose comme un HubConnection sans reconnexion automatique configurée.

L'approche de reconnexion offre la possibilité de :

  • Avertir les utilisateurs que la connexion a été perdue.
  • Désactivez 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, les transitions HubConnection reviennent à l'état Connected et déclenchent ses rappels onreconnected. Cela permet d'informer les utilisateurs que la connexion a été rétablie.

Étant donné que la connexion semble entièrement nouvelle connectionId pour le serveur, un nouveau est fourni au 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 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 HubConnection, le passe à l'état Disconnected et déclenche ses rappels onclose. C'est l'occasion d'informer les utilisateurs :

  • La connexion a été définitivement perdue.
  • Essayez 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. La configuration par défaut attend également zéro seconde 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 en utilisant la configuration par défaut.

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

Le délai de reconnexion configuré diffère 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.

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

Alternativement, un code peut être écrit qui reconnecte le client manuellement, comme illustré dans la section suivante.

Se 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
  • Conservation d’un verrou web
  • Tenir une serrure IndexedDB
  • Être connecté à un périphérique USB
  • Capture vidéo ou audio
  • Être en miroir
  • Capturer une fenêtre ou un affichage

L'heuristique du navigateur peut changer au fil du temps et peut différer d'un navigateur à l'autre. 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 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.
  • Le résolveur de promesse, lockResolver, est stocké afin que le verrou puisse être libéré lorsqu'il est acceptable que l'onglet dorme.
  • Lors de la fermeture de la connexion, le verrou est libéré en appelant lockResolver(). Lorsque le verrou est relâché, l'onglet est autorisé à dormir.

Ressources supplémentaires

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)

Installer le SignalR package 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 nouveau dossier nommé signalr 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 d'origine croisée

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 passés pour mapper invoke aux arguments user et message de la méthode hub :

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

Notes

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 la section Foire aux questions (référentiel 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.

Notes

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

Notes

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 catch de Promise 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. Journaux Error uniquement messages.
  • signalR.LogLevel.Warning: Messages d'avertissement sur les erreurs potentielles. Journaux Warning, et Error messages.
  • signalR.LogLevel.Information: Messages d'état sans erreur. Journaux Information, Warninget Error messages.
  • signalR.LogLevel.Trace: Tracer les messages. 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();

Reconnecter les clients

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

Étant donné que la connexion semble entièrement nouvelle connectionId pour le serveur, un nouveau sera fourni au rappel onreconnected.

Avertissement

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.

Se 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
  • Conservation d’un verrou web
  • Tenir une serrure IndexedDB
  • Être connecté à un périphérique USB
  • Capture vidéo ou audio
  • Être en miroir
  • Capturer une fenêtre ou un affichage

Notes

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.
  • Le résolveur de promesse (lockResolver) est stocké afin que le verrou puisse être libéré lorsqu'il est acceptable que l'onglet dorme.
  • Lors de la fermeture de la connexion, le verrou est libéré en appelant lockResolver(). Lorsque le verrou est relâché, l'onglet est autorisé à dormir.

Ressources supplémentaires