Loupe

Intégration de SignalR dans une application ASP.NET Core avec Angular 5

SignalR est une librairie proposée par Microsoft et qui permet aux développeurs ASP.NET, via les WebSockets, d'intégrer des fonctionnalités de "temps-réel" dans leurs applications Web (telles que des applications de type chat, graphiques boursiers, etc.).

Pour intégrer cette bibliothèque (encore en version Beta mais qui devrait être intégrée/finalisée dans la release de .NET Core 2.1) dans une application ASP.NET Core, c'est relativement simple et commence par l'ajout du package Nuget "Microsoft.AspNetCore.SignalR". Attention, pour cette partie, il faut bien penser à activer la case permettant d'avoir accès aux pré-versions:

Nuget.png

La dernière version disponible du package est la 1.0.0-preview1-final. Cependant, si lors des développements vous rencontrez l'erreur "HttpError: Method Not Allowed", alors je vous conseille de passer sur la version précédente (1.0.0-alpha2-final). L'API est légèrement différente entre les 2 versions (méthode InvokeAsync renommée en SendAsync, etc.) mais cela vous permettra d'arriver à vos fins dans l'implémentation de SignalR plutôt que de rester bloqués inutilement.

 

Configuration côté ASP.NET Core

Une fois le package Nuget ajouté, nous pouvons créer notre premier Hub, à savoir un simple classe qui hérite du type "Hub":

public class DataHub : Hub
{
    public void GetData()
    {
        Clients.All.InvokeAsync("getData", new Random().Next());
    }
}

Dans cette classe, nous définissons une (ou plusieurs méthodes) qui nous permettront de communiquer des données à l'ensemble des clients connectés (All). Vous avez également la possibilité de filtrer le (ou les) client(s) au(x)quel(s) faire parvenir les données:

Methods.PNG

Le premier paramètre de la méthode InvokeAsync est le nom de la méthode qui sera appelée sur les différents clients. Pour faire simple, vous pouvez voir cette phase comme le déclenchement d'un évènement sur l'ensemble des clients connectés ! Enfin, le deuxième paramètre de la méthode contient le (ou les) paramètre(s) qui seront envoyé(s) aux différents clients qui pourront ainsi récupérer la (ou les) valeur(s) envoyée(s) pour les traiter.

Une fois le Hub prêt, il faut maintenant passer à la configuration de l'application Web (Startup.cs). Nous allons commencer par ajouter SignalR dans la liste des services:

services.AddSignalR();

Ensuite, nous allons configurer le pipeline pour lui indiquer que nous souhaitons utiliser SignalR:

app.UseSignalR(builder =>
{
    builder.MapHub<DataHub>("data");
});

Lors de cette configuration, nous indiquons le chemin vers lequel le client devra pointer pour pouvoir se connecter au Hub que nous venons de créer (il sera alors nécessaire de se connecter sur http://votreserver/data).

Attention, pour que votre application Angular puisse se connecter avec succès à l'application, il ne faut pas oublier de mettre en place la politique CORS:

services.AddCors(options =>
{
     options.AddPolicy("CorsPolicy", builder =>
     {
          builder.AllowAnyHeader()
                  .AllowAnyMethod()
                  .WithOrigins("http://localhost:4200");
    });
});

app.UseCors("CorsPolicy");

Notre serveur étant maintenant configuré, nous pouvons passer à la configuration côté client ! 

 

Configuration côté Angular 5

La première chose à faire est d'installer le package NPM de SignalR:

npm install @aspnet/signalr-client

A présent, dans votre composant, vous pouvez démarrer une connexion à votre Hub (idéalement, je rappelle que l'URL de connexion devrait être externalisée mais ce n'est pas l'objet de cet article):

ngOnInit(): void {
    this.hubConnection = new HubConnection('http://localhost:49750/data');
    this.hubConnection
      .start()
      .then(() => this.toastr.success('Connecté au hub'))
      .catch(error => this.toastr.error('Problème de connexion au hub: ' + error));
  }

Une fois la connexion réalisée, nous pouvons nous abonner à la réception des messages envoyés par le serveur, en gardant la même syntaxe que ce que l'on connait en JavaScript / TypeScript:

this.hubConnection.on('getData', args => {
    this.toastr.success('Nouvelle valeur reçue: ' + args);
});

Il ne reste plus qu'à faire en sorte de déclencher ce message (au moyen de la méthode existante sur le Hub) et cela est possible grâce à la méthode invoke de l'objet HubConnection:

sendMessage(): void {
   this.hubConnection.invoke('GetData');
}

Bien sûr, et même si je ne l'ai pas montré, il est possible de faire en sorte de passer des paramètres à la méthode du Hub, pour que ceux-ci soient interceptés/modifiés et renvoyés aux différents clients.

A présent, il ne reste plus qu'à exécuter le serveur (qui héberge le Hub) et l'application cliente (sur autant de devices/navigateurs que vous le souhaitez) pour être en mesure d'envoyer un message depuis un client à votre Hub et faire en sorte que ce message soit broadcasté à un client spécifique, à tous les clients, etc.

Results.png

Si vous voulez faire en sorte que votre message soit broadcasté à l'ensemble des clients SAUF celui qui est à son origine, vous pouvez utiliser la propriété Context.ConnectionId (sur le Hub) pour récupérer la connexion du client qui invoque la méthode du Hub et, ainsi, filtrer les clients:

Clients.AllExcept(new List<string> { Context.ConnectionId }).InvokeAsync("getData", new Random().Next());

 

Bonus Point : Invoquer une méthode de votre Hub depuis un controlleur

Depuis un controller, service, etc., vous pouvez utiliser l'interface IHubContext pour récupérer une référence vers l'un de vos Hub et ainsi appeller l'une de ces méthodes:

[Produces("application/json")]
[Route("api/Values")]
public class ValuesController : Controller
{
    private readonly IHubContext<DataHub> _hubContext;

    public ValuesController(IHubContext<DataHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public IActionResult Get()
    {
        _hubContext.Clients.All.InvokeAsync("getData", new Random().Next());

        return Ok();
    }
}

 

Et voilà! En quelques lignes de code, vous pouvez ajouter des fonctionnalités de temps réel, multi-navigateurs, multi-devices, à vos applications Web!

 

Happy coding! :)

 

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus