Jan


Dans cet article, nous allons détailler les concepts de SignalR et proposer un cas d’utilisation au sein d’une application .NET Core.

Notre cas d’utilisation prendra la forme d’une application d’enchères où différents clients pourront miser et seront informés instantanément des autres mises. Si vous souhaitez nous accompagner dans l’élaboration de cette application, vous pouvez générer un projet .NET Core console vide pour le moment.

Avant de coder, laissons place à un peu de théorie, nous reviendrons au fur et à mesure de l’article sur les étapes de notre application d’enchères.

SignalR : du temps réel, de la communication et de la sécurité

SignalR est une bibliothèque open source qui facilite la communication en temps réel entre le client et le serveur. Le temps réel permet au serveur de proposer du contenu aux clients connectés sans avoir besoin d’attendre une requête provenant de ces clients. Cela s’avère très pratique pour des applications qui ont besoin de mises à jour régulières auprès du serveur comme par exemple les réseaux sociaux, les applications de collaboration ou les jeux en ligne.

Les fonctionnalités proposées par SignalR sont diverses, il y a la gestion des connexions, l’envoi de messages simultanés à tous les clients connectés ou à une partie des clients correspondant à un rôle spécifique défini. De plus, la bibliothèque propose aussi une mise à l’échelle pour votre application en fonction de vos usages.

L’intérêt de cette mise à l’échelle est d’améliorer les performances de votre application dans le cas d’un trafic élevé, en ajoutant d’autres serveurs pour supporter la charge, nous parlons ici de scale out. Le problème que vous pouvez rencontrer avec cette mise à l’échelle, c’est que les clients qui sont connectés à un serveur en particulier ne pourront pas communiquer avec ceux qui sont connectés sur d’autres serveurs. Pour pallier ce problème, il existe plusieurs solutions comme la configuration d’Azure SignalR service ou de Redis.

Pour mettre en place cette communication en temps réel, SignalR va s’appuyer sur différentes technologies comme les web sockets ou le hub par exemple.

Nous détaillerons petit à petit ces différents concepts.

Pourquoi le mettre en place dans votre application web ?

Beaucoup de raisons peuvent vous pousser à utiliser SignalR, l’une des principales étant la simplicité d’utilisation et l’abstraction de complexité que propose cette bibliothèque.

SignalR vous fournit différentes API permettant de gérer les Remote Procedure Call (RPC). Le protocole RPC est un moyen de communication entre un programme qui a besoin de demander un service à un autre programme, hébergé sur une autre machine dans le réseau sans avoir besoin de connaître les détails du réseau courant. Ce qu’il faut retenir pour notre article, c’est que les appels RPC vont nous permettre d’appeler les méthodes du serveur depuis les clients connectés et inversement.

Cette bibliothèque prend également en charge la gestion des connexions et surtout elle optimise le mode de transport en choisissant celui qui est le plus optimal en fonction du navigateur.

Rentrons un peu plus en détail sur ce dernier point.

SignalR s’adapte vite, très vite

Nous avons vu précédemment que SignalR était capable de choisir le mode de transport le plus adapté et pour le trouver il adoptera la stratégie suivante :

Tout d’abord, SignalR choisira les WebSockets parce que c’est son mode de transport préféré et celui qui est le plus optimisé pour lui.
Le protocole réseau websocket, qui est basé sur le protocole TCP, permet d’établir une connexion bi directionnelle entre un serveur et un client. Son principal avantage est le chargement rapide des données. Contrairement à une requête HTTP où le client doit faire une requête au serveur pour chaque demande, ce protocole permet de garder une connexion ouverte entre les deux et ainsi il est possible d’envoyer des données dynamiquement au client sans qu’il ait besoin de faire de requêtes.

Mais il peut faire preuve d’infidélité et choisir un autre mode de transport qui sera plus adapté au client et au serveur.
Par exemple, dans le cas d’un très vieux navigateur (IE 6 ou Firefox 2…), il optera pour d’autres modes de transport comme le Server-Sent Events, le Forever Frame ou le Long Polling.

Le Server-Sent Events établit une connexion unidirectionnelle serveur/client basée sur le protocole HTTP. Pour une communication à partir du serveur, il crée un canal Event source et à chaque fois que le serveur a besoin d’envoyer des données au client, il utilisera ce canal. A noter qu’il n’est pas compatible avec Internet Explorer.

Le Forever Frame établit une connexion unidirectionnelle serveur/client. Il crée une iframe cachée entre le serveur et le client. Comme le mode de transport SSE, la connexion reste ouverte en permanence. Ce mode de transport présente l’avantage d’être compatible avec Internet Explorer.

Le Long Polling permet également d’établir une connexion unidirectionnelle entre un serveur et un client, son principal avantage est qu’il fonctionne bien avec les anciens navigateurs. Iil présente tout de même un grand désavantage ; une fois que la connexion a été utilisée, elle sera vite fermée et si le client souhaite communiquer de nouveau vers le serveur, il doit rétablir la connexion. Il sera une solution de repli dans le cas où les autres modes de transports ne sont pas compatibles.

schéma détaillé concernant le choix du transporteur<

SignalR, le transporteur

Comme vu plus haut, SignalR vous propose plusieurs modes de transport.
Par défaut, nous déléguons la responsabilité à SignalR de choisir à notre place le mode que le navigateur du client peut comprendre. Cette étape s’appelle la négociation.
Cela a comme avantage de rendre un maximum de navigateurs compatibles avec notre application. L’inconvénient cependant réside dans le temps qu’il faut à la librairie front de SignalR pour déterminer le bon mode de transport afin de communiquer avec notre hub.

Pour cela, nous pouvons imposer à SignalR un mode de transport.
Et cette manipulation est rendue possible via la ligne de code suivante :

connection.start({ transport: 'longPolling' });

Dans cet exemple, nous imposons comme mode de transport le LongPolling (voir 2.x). Ainsi, tous les browsers qui accéderont à l’application utiliseront ce dernier comme mode. Bien sûr nous pouvons combiner plusieurs méthodes de transport :

connection.start({ transport: ['longPolling','foreverFrame'] });

Dans cette situation, le transport utilisera le premier index (LongPolling) comme mode favori, et en cas d’échec d’envoi pour différentes raisons telle que le browser qui ne sait pas utiliser ce mode spécifique de transport, nous utiliserons en secours le mode ForeverFrame.

Dans le cadre de notre application nous laisserons SignalR choisir le mode de transport le plus adapté à notre contexte.

Comment ça marche côté back ?

l’API SignalR vient avec une classe abstraite nommée . Cette dernière va nous permettre de contenir nos méthodes qui seront appelées côté client et d’établir une référence vers les clients enregistrés. Ainsi elle sera notre attache coté back afin de communiquer avec les autres composants backend. De plus, le hub nous permet d’appeler les fonctions créées côté client.

Voici un graphe permettant de comprendre la hiérarchisation des couches :

source : docs.microsoft.com

 

Concernant la communication du back vers le front, le front utilise le RPC (remote process call).
Depuis .Net Core 3, SignalR est inclus, donc il n’est pas nécessaire de le récupérer depuis NuGet (Microsoft.AspNetCore.SignalR).

Comment créer un hub ?

Premièrement, il faut que vous ajoutiez dans le container de service (votre main, ici Program.cs) la ligne :

builder.Services.AddSignalR();

et que sur votre middleware vous ajoutiez pour le endpoint l’enregistrement de SignalR :

app.MapHub("/demoKaibayHub");

Concernant notre Hub, ce dernier doit hériter de la classe abstraite Hub :

public class DemoKaibayHub : Hub

A la création de nos méthodes, nous avons accès notamment à deux paramètres qui nous seront utiles : Clients et Context.

  • Clients nous permet de communiquer avec nos clients par le biais notamments de ces méthodes :
  • All() : appelle la méthode pour tous les clients exemple : une alerte de mise à jour d’un site.
  • Caller() : appelle la méthode du client qui l’a invoqué par exemple : une notification d’achat avec succès.
  • Other() : appelle la méthode de tous les clients sauf celui qui a invoqué le hub par exemple : notification des autres utilisateurs d’un enchérisseur.
  • Group(idGroup) : appelle la méthode des utilisateurs d’un groupe exemple: notification des utilisateurs dans un article différent;
  • Groups() : appelle tous les groupes connue du Hub
  • User() : méthode permettant de pouvoir interagir avec l’utilisateur actif, comme par exemple l’auto-notifications.
  • Context, quant à lui, permet d’avoir des informations sur l’utilisateur qui a invoqué le hub, l’id de connexion et bien d’autres.

Pour notre application,

Nous avons besoin d’une méthode pour créer des mises :

public async Task BidRequest(int auctionId, double priceBid)

Et dans cette méthode, nous allons simplement utiliser le mot clé Clients avec la propriété All pour avertir tous les clients d’une mise à jour de l’enchère.

await Clients.All.SendAsync("UpdatePrice", auctionId, priceBid);

Communication entre le backend et le frontend

Précédemment, nous avons vu comment mettre en place notre hub et nous avons expliqué que du côté client comme du côté serveur SignalR utilise RPC.

Maintenant nous allons expliquer les différents modes d’interaction client – serveur.

Dans notre situation, nous souhaitons envoyer une notification ainsi que le changement du prix de notre enchère, pour cela l’api de SignalR nous propose différentes interactions.

Pour notre partie front, nous utiliserons JavaScript. Voici le package client permettant de communiquer avec le Hub :

npm install @microsoft/signalr

Pour la gestion des dépendances JavaScript, nous utiliserons LibMan:

npm install @microsoft/dotnet tool install -g Microsoft.Web.LibraryManager.Cli

Afin que le frontend puisse communiquer avec le hub situé dans le backend, nous avons besoin d’utiliser la librairie importée pour lui donner la direction vers notre hub.

libman install @microsoft/signalr@latest -p unpkg -d wwwroot/js/signalr --files dist/browser/signalr.js --files dist/browser/signalr.min.js

Le code ci-dessous générera un hubproxy connaissant au préalable les différentes méthodes contenues dans le hub mais aussi l’intelligence d’interagir avec, grâce au RPC :

var connection = new signalR.HubConnectionBuilder().withUrl("/demoKaibayHub").build();
connection.start().then(() => {
console.log("SignalR Connected.");
});


Schéma illustrant comment SignalR communique entre le back et le front

 

Un petit clic vaut mieux qu’un grand choc

Ce célèbre slogan des années 70 est toujours d’actualité et SignalR l’a bien compris.

Pour vous aider à gérer la sécurité de votre hub, il vous propose de sécuriser vos méthodes en les décorant de l’attribut Authorize. Cet attribut bloque l’accès aux utilisateurs non authentifiés. Vous pouvez personnaliser cet attribut afin d’établir des stratégies en fonction des différents groupes d’utilisateurs qui peuvent accéder ou non aux méthodes exposées.

Nous allons appliquer cet attribut directement sur notre méthode BidRequest :

[Authorize]
public async task BidRequest(int auctionId, double priceBid)
[Authorize("admin")]
public void MyOtherMethod()

Vous pouvez aussi appliquer cet attribut directement sur le hub :

[Authorize]
public class DemoKaibayHub : Hub

Ce qu’il faut retenir

SignalR permet de prendre en main le temps réel dans les applications en abstrayant le plus possible les concepts clés comme le transport et facilite les échanges entre les différentes couches en fournissant des méthodes adaptées.

Pour aller plus loin

Dans cet article, nous n’avons illustré que les grands concepts de SignalR mais ne vous inquiétez pas, vous pouvez vous familiariser avec l’outil en récupérant notre projet clé en main sur GitHub et commencer à enchérir :
https://github.com/KAIBEE/Kaibay

Quelques sources en vrac

Concernant les méthodes du client :
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.ihubclients

Concernant le context :
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.hubcallercontext

Pour avoir plus d’informations sur les Hub voici un lien utile :
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.hub?view=aspnetcore-6.0

Sofiane OUARAB, Développeur .NET Full stack
Dylan SMILA, Développeur .NET & Ingénieur DevOps

Related Posts

Leave A Comment