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. JournauxError
uniquement messages.signalR.LogLevel.Warning
: Messages d'avertissement sur les erreurs potentielles. JournauxWarning
, etError
messages.signalR.LogLevel.Information
: Messages d'état sans erreur. JournauxInformation
,Warning
etError
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 rappelsonreconnecting
. - Ne passe pas à l'état
Disconnected
et ne déclenche pas ses rappelsonclose
comme unHubConnection
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 :
- Une fonction (dans ce cas, la fonction
start
) est créée pour démarrer la connexion. - Appelez la fonction
start
dans le gestionnaire d'événementsonclose
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
- Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
- Informations de référence sur l’API JavaScript
- Tutoriel JavaScript
- Tutoriel WebPack et TypeScript
- Hubs
- Client .NET
- Publication dans Azure
- Requêtes d'origine croisée (CORS)
- Documentation serverless du service SignalR Azure
- Résoudre les erreurs de connexion
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. JournauxError
uniquement messages.signalR.LogLevel.Warning
: Messages d'avertissement sur les erreurs potentielles. JournauxWarning
, etError
messages.signalR.LogLevel.Information
: Messages d'état sans erreur. JournauxInformation
,Warning
etError
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 :
- Une fonction (dans ce cas, la fonction
start
) est créée pour démarrer la connexion. - Appelez la fonction
start
dans le gestionnaire d'événementsonclose
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.