Le Web temps réel en ASP.NET avec SignalR

signalrlogoOn est parfois confrontés à des situations où, dans une application Web, le serveur doit pousser en temps réel des données à ses clients connectés. Par exemple : un service de notification, un tableau de bord ou même un service de chat.

Avant les Websockets HTML5, plusieurs techniques Javascript pouvaient être utilisées côté client pour simuler l’effet push/temps réel du serveur : le client envoie continuellement des requêtes au serveur pour voir s’il a de nouveaux messages qui lui sont destinés  (utilisation du XMLHttpRequest et de fonctions Javascript tels que setInterval ou setTimeout). On se retrouvait souvent à maintenir un certain nombre de lignes de code écrites en Javascript.

 Les Websockets offrent une réponse au polling client en assurant un canal bidirectionnel et full-duplex entre le client et le serveur.

Pourquoi SignalR?

SignalR est une bibliothèque client/serveur intégrée fournissant toute la plomberie nécessaire pour ajouter des fonctionnalités temps-réel à une application Web ASP.NET.

Cette bibliothèque se base sur les Websockets. Quand ces derniers ne sont pas gérés par le navigateur du client, la librairie offre une solution de fallback en utilisant d’autres techniques sans avoir à changer le code de l’application côté client et serveur. SignalR va, en effet, masquer toute la complexité liée à la gestion des appels Javascripts au serveur. Il va, également, permettre l’appel de fonctions Javascript clients à partir du serveur.

Découvrons quelques fonctionnalités de SignalR avec un scénario tout simple : Notifier les clients connectés sur le site en temps réel à partir d’une interface d’administration.

On va tout d’abord commencer par créer un nouveau projet ASP.NET MVC4 vide et après récupérer SignalR via Nuget.

aspnetmvc4emptyproject

En utilisant la console du gestionnaire d’extension Nuget lancer la commande

Install-package Microsoft.AspNet.SignalR –pre

Cette commande va faire le nécessaire pour récupérer toutes les dépendances dont on aura besoin pour pouvoir utiliser SignalR dans le projet.

nugetsignalr

Notez l’utilisation du paramètre –pre. SignalR est, à la date d’écriture de cet article, en version release candidate.

Création d’un Hub

Un Hub est une classe de l’API SignalR côté serveur. Elle va se charger de gérer des appels clients vers le serveur et inversement. Dans notre exemple, nous allons créer un Hub où nous définissons la méthode que l’administrateur doit appeler pour notifier tous les clients connectés sur le site.

Regardons de plus près la classe « Notifier »

public class Notifier : Hub
{
     public void NotifyAllUsers(Notification message)
     {
         Clients.All.Broadcast(message);
     }
}

Rien de bien compliqué. Cette classe hérite de Hub et définit une méthode publique NotifyAllUsers.  En définissant cette méthode, nous indiquons à SignalR que cette méthode pourra être invoquée par le client.  La propriété Clients est héritée de Hub. Elle encapsule des informations  concernant les connexions SignalR. All, comme son nom l’indique, représente toutes connexions clientes. La méthode Broadcast va invoquer à partir du serveur la fonction du même nom côté client avec le paramètre message.

Pour cet exemple, une notification est une classe très simple :

public class Notification
{
        // le type de la notification
        public NotificationType Type { get; set; }
        // en millisecondes, durée pendant laquelle la notification est affichée
        public int Duration { get; set; }
        // le texte de la notification
        public string Text { get; set; }
}
public enum  NotificationType
{
        Alert = 0,
        Notice = 1,
        Greeting = 2,
        HotNews = 3
}

Tout aspect lié à la sérialisation/désérialisation de l’objet représentant le message est géré par SignalR.

Notez que la property All de Clients est de type dynamic :

signalrdynamicexpression

En définissant ce Hub côté serveur, SignalR va se charger, lors du démarrage de l’application, de générer un proxy en Javascript contenant toute la mécanique nécessaire pour assurer l’interaction client/serveur avec ce Hub.

Définissons maintenant les vues administrateur et utilisateurs.

Vue administrateur

Afin de simplifier l’exemple, tous les aspects liés à la création du contrôleur et à l’authentification de l’administrateur n’ont pas été traités dans cet article.

Commençons par créer une vue pour la page d’administration :

createviewfromcontrolleraction

Dans la vue administrateur, nous allons définir les éléments nécessaires nous permettant d’invoquer la méthode NotifyAllUsers du Notifier Hub.

Tout passe par Javascript ! Rien de bien compliqué, il suffit de référencer ces scripts dans la vue :

<script type="text/javascript" src="~/scripts/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="~/scripts/jquery.signalR-1.0.0-rc2.min.js"></script>
<script type="text/javascript" src="~/signalr/hubs"></script>

Notez l’importation de ce script “~/signalr/hubs”. A aucun moment on ne l’a défini ou ajouté dans le projet. Ce script contient, en effet, tous les proxys Javascript des Hubs créés côté serveur. Comme mentionné plus tôt dans l’article, SignalR va se charger de la création des proxys et de ce script pour nous.

Et enfin, Voici le script qui va nous permettre d’appeler la méthode NotifyAllUsers du Hub

<script type="text/javascript">
        $(function () {
            // proxy correspondant au Hub Notifier défini côté serveur
            var notifier = $.connection.notifier
            // /!\ Important: Démarrer toutes les connexions aux Hubs
            // définis côté serveur
            $.connection.hub.start().done(function () {
                $('#send').click(function () {
                    // Construction de la notification en Json
                    var notification =
                    {
                        Text: "Hello world",
                        Duration: 5000,
                        Type: 2
                    };
                    // invocation de la méthode NotifiyAllUsers
                    // définie dans le Hub Notifier
                    notifier.server.notifyAllUsers(notification);
                });
            });
        });
</script>

$.connection.notifier fait référence au Hub créé côté serveur. En effet, si vous regardez le contenu de “/signalr/hub” vous allez trouver un Javascript. Ce script a été généré dynamiquement par SignalR à partir du ou des Hubs créés côté serveur.

Le format Json est utilisé pour définir une notification.

Vues utilisateurs

Pour afficher les notifications destinées à tous les utilisateurs du site, il est plus logique de voir apparaître les notifications sur toutes les pages. Je vous recommande d’écrire le Javascript destiné aux pages utilisateurs dans le Layout (si vous utilisez Razor comme view engine) des vues utilisateurs (Master page si vous avez opté pour le view engine classique aspx).

Revoyons ce que nous avons défini dans notre Hub.

Clients.All.Broadcast(message);

Afin de publier le message à tous les clients connectés, nous devons déclarer côté client Javascript la méthode que le serveur va invoquer:

<script src="~/scripts/jquery-1.6.4.min.js"></script>
<script src="~/scripts/jquery.signalR-1.0.0-rc2.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
    $(function () {
        // proxy correspondant au Hub Notifier défini côté serveur
        var notifier = $.connection.notifier;
        // Déclaration de la fonction que le serveur pourra invoquer
        notifier.client.broadcast = function (message) {
            $("#text").html(message.Text); // mettre à jour le contenu du bloc destiné à afficher le message
            $('#notification').show(1000) // afficher la notification
                .delay(message.Duration) // durée spécifiée dans le message envoyé
                .hide(1000); // cacher le message après 'message.Duration' millisecondes
        };
        // Important : démarrer toutes les connexions aux Hubs
        // déclarés côté serveur
        $.connection.hub.start();
    });
</script>
<div id="notification" class="notificationStyle">
    <div id="text"></div>
</div>
<style>
        .notificationStyle
        {
            background:#e9e9e9;
            width:250px;
            border-style:solid;
            border-width:2px;
            border-color:#e1e1e1;
            height:100px;
            overflow:hidden;
            box-shadow: 0px 0px 10px 0px #656565;
            display:none;
        }
</style>

Définition de la route pour le Hub

Une route doit être définie pour le ou les Hubs créés. Il nous faudra la définir dans la méthode Application_Start du Global.asax. Cela va permettre d’enregistrer la route “~/signalr”.

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            // Définition de la route par défaut
            RouteTable.Routes.MapHubs();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }

Vous pouvez, bien-entendu, choisir un autre nom pour la route. Il ne faudra pas  dans ce cas oublier de renommer les références vers le script /signalr/hub avec le nom que vous aurez choisi

Pour aller plus loin

Comme vous avez pu le constater, mettre en place une application ASP.NET MVC avec des fonctionnalités push “temps réel” est facile avec SignalR. Le code côté client et serveur se retrouvent réduits.

SignalR offre d’autres fonctionnalités intéressantes, que je n’ai pas abordées dans cet article, telles que : La prise en charge de groupes de clients, la détection des connexions et déconnexions des clients et la gestion des autorisations.

J’invite les plus curieux d’entre vous à regarder de plus près cette API !

Vous trouverez ici le code de cet article

Quelques ressources

Le site officiel de ASP.NET SignalR

le github de SignalR

Des exemples sur le github de SignalR

Pages de SignalR sur le site www.asp.net

Une présentation très intéressante qui a eu lieu au du Build 2012

Pour suivre @SignalR sur Twitter

Nombre de vue : 1930

COMMENTAIRES 4 commentaires

  1. guy dit :

    Merci pour le partage.
    C’est toujours intéressant de savoir qu’il y’a des alternatives aux WebSocket. Par contre je serais intéresser de savoir quelle techno se cache derrière les fallback utilisés. n’y a til pas de risques de sécurité (déni de service) comme ce fut le cas avec les Websockets ?

  2. Zied BEN TAHAR dit :

    Guy,
    SignalR n’est pas une alternative aux Websockets. Cette API permet, en effet, d’ajouter des fonctionnalités Requêtes/Réponses et Push serveur plus facilement et surtout plus rapidement sans que nous ayons à développer toute la mécanique par nous même.
    SignalR va utiliser les Websockets quand c’est supporté:
    Quand les Websockets ne sont pas gérées côté client ou serveur, forever-frame ou long polling seront utilisées comme techniques de fallback.

  3. Florian Béclin dit :

    Merci beaucoup pour ce partage.
    Je ne connaissais pas et ça a l’air vraiment très intéressant.

    Je n’ai pas encore eu à avoir à faire du temps réel sur mes projets, mais lorsque cela sera le cas, je pencherai dessus 🙂

  4. McSoda dit :

    Excellent.

    Il faut juste faire quelques modifs de code pour adapter à SignalR 2.0.0 (cf expmlication à http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/upgrading-signalr-1x-projects-to-20).

AJOUTER UN COMMENTAIRE