ASP.NET Guide de l’API SignalR Hubs - Serveur (SignalR 1.x)

par Patrick Fletcher, Tom Dykstra

Avertissement

Cette documentation ne concerne pas la dernière version de SignalR. Jetez un coup d’œil à ASP.NET Core SignalR.

Ce document fournit une introduction à la programmation côté serveur de l’API ASP.NET SignalR Hubs pour SignalR version 1.1, avec des exemples de code illustrant des options courantes.

L’API SignalR Hubs vous permet d’effectuer des appels de procédure distante (RPC) à partir d’un serveur vers des clients connectés et de clients vers le serveur. Dans le code du serveur, vous définissez des méthodes qui peuvent être appelées par les clients, et vous appelez des méthodes qui s’exécutent sur le client. Dans le code client, vous définissez des méthodes qui peuvent être appelées à partir du serveur et vous appelez des méthodes qui s’exécutent sur le serveur. SignalR prend en charge l’ensemble de la plomberie client à serveur pour vous.

SignalR propose également une API de niveau inférieur appelée Connexions persistantes. Pour une présentation de SignalR, hubs et connexions persistantes, ou pour un tutoriel qui montre comment créer une application SignalR complète, consultez SignalR - Prise en main.

Vue d’ensemble

Ce document contient les sections suivantes :

Pour obtenir de la documentation sur la programmation des clients, consultez les ressources suivantes :

Les liens vers les rubriques de référence d’API sont vers la version .NET 4.5 de l’API. Si vous utilisez .NET 4, consultez la version .NET 4 des rubriques de l’API.

Comment inscrire l’itinéraire SignalR et configurer les options SignalR

Pour définir l’itinéraire que les clients utiliseront pour se connecter à votre hub, appelez la méthode MapHubs au démarrage de l’application. MapHubs est une méthode d’extension pour la System.Web.Routing.RouteCollection classe . L’exemple suivant montre comment définir l’itinéraire SignalR Hubs dans le fichier Global.asax .

using System.Web.Routing;
public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
    }
}

Si vous ajoutez la fonctionnalité SignalR à une application ASP.NET MVC, assurez-vous que l’itinéraire SignalR est ajouté avant les autres itinéraires. Pour plus d’informations, consultez Tutoriel : Prise en main avec SignalR et MVC 4.

The /signalr URL

Par défaut, l’URL d’itinéraire que les clients utiliseront pour se connecter à votre hub est « /signalr ». (Ne confondez pas cette URL avec l’URL « /signalr/hubs », qui correspond au fichier JavaScript généré automatiquement. Pour plus d’informations sur le proxy généré, consultez Guide de l’API SignalR Hubs - Client JavaScript - Le proxy généré et ce qu’il fait pour vous.)

Il peut y avoir des circonstances extraordinaires qui rendent cette URL de base non utilisable pour SignalR ; Par exemple, vous avez un dossier dans votre projet nommé signalr et vous ne souhaitez pas modifier le nom. Dans ce cas, vous pouvez modifier l’URL de base, comme indiqué dans les exemples suivants (remplacez « /signalr » dans l’exemple de code par l’URL souhaitée).

Code du serveur qui spécifie l’URL

RouteTable.Routes.MapHubs("/signalr", new HubConfiguration());

Code client JavaScript qui spécifie l’URL (avec le proxy généré)

$.connection.hub.url = "/signalr"
$.connection.hub.start().done(init);

Code client JavaScript qui spécifie l’URL (sans le proxy généré)

var connection = $.hubConnection("/signalr", { useDefaultPath: false });

Code client .NET qui spécifie l’URL

var hubConnection = new HubConnection("http://contoso.com/signalr", useDefaultUrl: false);

Configuration des options SignalR

Les surcharges de la MapHubs méthode vous permettent de spécifier une URL personnalisée, un programme de résolution de dépendances personnalisé et les options suivantes :

  • Activer les appels inter-domaines à partir de clients de navigateur.

    En règle générale, si le navigateur charge une page à partir de http://contoso.com, la connexion SignalR se trouve dans le même domaine, à l’emplacement http://contoso.com/signalr. Si la page de http://contoso.com établit une connexion à http://fabrikam.com/signalr, il s’agit d’une connexion inter-domaines. Pour des raisons de sécurité, les connexions inter-domaines sont désactivées par défaut. Pour plus d’informations, consultez ASP.NET Guide de l’API SignalR Hubs - Client JavaScript - Comment établir une connexion inter-domaines.

  • Activez les messages d’erreur détaillés.

    Lorsque des erreurs se produisent, le comportement par défaut de SignalR consiste à envoyer aux clients un message de notification sans détails sur ce qui s’est passé. L’envoi d’informations détaillées sur les erreurs aux clients n’est pas recommandé en production, car des utilisateurs malveillants peuvent être en mesure d’utiliser ces informations dans des attaques contre votre application. Pour la résolution des problèmes, vous pouvez utiliser cette option pour activer temporairement un rapport d’erreurs plus informatif.

  • Désactivez les fichiers proxy JavaScript générés automatiquement.

    Par défaut, un fichier JavaScript avec des proxys pour vos classes Hub est généré en réponse à l’URL « /signalr/hubs ». Si vous ne souhaitez pas utiliser les proxys JavaScript ou si vous souhaitez générer ce fichier manuellement et faire référence à un fichier physique dans vos clients, vous pouvez utiliser cette option pour désactiver la génération de proxy. Pour plus d’informations, consultez Guide de l’API SignalR Hubs - Client JavaScript - Comment créer un fichier physique pour le proxy généré par SignalR.

L’exemple suivant montre comment spécifier l’URL de connexion SignalR et ces options dans un appel à la MapHubs méthode . Pour spécifier une URL personnalisée, remplacez « /signalr » dans l’exemple par l’URL que vous souhaitez utiliser.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableCrossDomain = true;
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);

Comment créer et utiliser des classes Hub

Pour créer un hub, créez une classe qui dérive de Microsoft.Aspnet.Signalr.Hub. L’exemple suivant montre une classe Hub simple pour une application de conversation.

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

Dans cet exemple, un client connecté peut appeler la NewContosoChatMessage méthode , et quand c’est le cas, les données reçues sont diffusées à tous les clients connectés.

Durée de vie de l’objet Hub

Vous n’instanciez pas la classe Hub ou n’appelez pas ses méthodes à partir de votre propre code sur le serveur ; tout ce qui est fait pour vous par le pipeline SignalR Hubs. SignalR crée une nouvelle instance de votre classe Hub chaque fois qu’il doit gérer une opération Hub, par exemple lorsqu’un client se connecte, se déconnecte ou effectue un appel de méthode au serveur.

Étant donné que les instances de la classe Hub sont temporaires, vous ne pouvez pas les utiliser pour conserver l’état d’un appel de méthode à l’autre. Chaque fois que le serveur reçoit un appel de méthode d’un client, une nouvelle instance de votre classe Hub traite le message. Pour conserver l’état via plusieurs connexions et appels de méthode, utilisez une autre méthode, telle qu’une base de données ou une variable statique sur la classe Hub, ou une autre classe qui ne dérive pas de Hub. Si vous conservez des données en mémoire, à l’aide d’une méthode telle qu’une variable statique sur la classe Hub, les données seront perdues lorsque le domaine d’application sera recyclé.

Si vous souhaitez envoyer des messages aux clients à partir de votre propre code qui s’exécute en dehors de la classe Hub, vous ne pouvez pas le faire en instanciant une classe Hub instance, mais vous pouvez le faire en obtenant une référence à l’objet de contexte SignalR pour votre classe Hub. Pour plus d’informations, consultez Comment appeler des méthodes clientes et gérer des groupes en dehors de la classe Hub plus loin dans cette rubrique.

Casse mixte des noms hub dans les clients JavaScript

Par défaut, les clients JavaScript font référence à Hubs à l’aide d’une version à casse mixte du nom de classe. SignalR effectue automatiquement cette modification afin que le code JavaScript puisse être conforme aux conventions JavaScript. L’exemple précédent est appelé dans contosoChatHub le code JavaScript.

Serveur

public class ContosoChatHub : Hub

Client JavaScript utilisant le proxy généré

var contosoChatHubProxy = $.connection.contosoChatHub;

Si vous souhaitez spécifier un autre nom pour les clients à utiliser, ajoutez l’attribut HubName . Lorsque vous utilisez un HubName attribut, aucun changement de nom n’est apporté à la casse mixte sur les clients JavaScript.

Serveur

[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub

Client JavaScript utilisant le proxy généré

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

Plusieurs hubs

Vous pouvez définir plusieurs classes Hub dans une application. Dans ce cas, la connexion est partagée, mais les groupes sont distincts :

  • Tous les clients utilisent la même URL pour établir une connexion SignalR avec votre service (« /signalr » ou votre URL personnalisée si vous en avez spécifié une), et cette connexion est utilisée pour tous les hubs définis par le service.

    Il n’existe aucune différence de performances pour plusieurs hubs par rapport à la définition de toutes les fonctionnalités du hub dans une seule classe.

  • Tous les hubs obtiennent les mêmes informations de requête HTTP.

    Étant donné que tous les hubs partagent la même connexion, les seules informations de requête HTTP que le serveur obtient sont celles fournies dans la requête HTTP d’origine qui établit la connexion SignalR. Si vous utilisez la demande de connexion pour transmettre des informations du client au serveur en spécifiant une chaîne de requête, vous ne pouvez pas fournir de chaînes de requête différentes à différents hubs. Tous les hubs recevront les mêmes informations.

  • Le fichier de proxys JavaScript généré contiendra des proxys pour tous les hubs dans un seul fichier.

    Pour plus d’informations sur les proxys JavaScript, consultez Guide de l’API SignalR Hubs - Client JavaScript - Le proxy généré et ce qu’il fait pour vous.

  • Les groupes sont définis dans hubs.

    Dans SignalR, vous pouvez définir des groupes nommés à diffuser vers des sous-ensembles de clients connectés. Les groupes sont gérés séparément pour chaque hub. Par exemple, un groupe nommé « Administrateurs » inclut un ensemble de clients pour votre ContosoChatHub classe, et le même nom de groupe fait référence à un ensemble différent de clients pour votre StockTickerHub classe.

Comment définir des méthodes dans la classe Hub que les clients peuvent appeler

Pour exposer une méthode sur le hub que vous souhaitez appeler à partir du client, déclarez une méthode publique, comme illustré dans les exemples suivants.

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}
public class StockTickerHub : Hub
{
    public IEnumerable<Stock> GetAllStocks()
    {
        return _stockTicker.GetAllStocks();
    }
}

Vous pouvez spécifier un type de retour et des paramètres, y compris des types et des tableaux complexes, comme vous le feriez dans n’importe quelle méthode C#. Toutes les données que vous recevez dans les paramètres ou que vous retournez à l’appelant sont communiquées entre le client et le serveur à l’aide de JSON, et SignalR gère automatiquement la liaison d’objets complexes et de tableaux d’objets.

Casse mixte des noms de méthode dans les clients JavaScript

Par défaut, les clients JavaScript font référence aux méthodes Hub à l’aide d’une version à casse mixte du nom de la méthode. SignalR effectue automatiquement cette modification afin que le code JavaScript puisse être conforme aux conventions JavaScript.

Serveur

public void NewContosoChatMessage(string userName, string message)

Client JavaScript utilisant le proxy généré

contosoChatHubProxy.server.newContosoChatMessage($(userName, message);

Si vous souhaitez spécifier un autre nom pour les clients à utiliser, ajoutez l’attribut HubMethodName .

Serveur

[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)

Client JavaScript utilisant le proxy généré

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

Quand s’exécuter de façon asynchrone

Si la méthode est longue ou qu’elle doit effectuer un travail qui impliquerait l’attente, comme une recherche de base de données ou un appel de service web, rendez la méthode Hub asynchrone en retournant un objet Task (à la place du void retour) ou un objet Task<T> (à la place du type de T retour). Lorsque vous retournez un Task objet à partir de la méthode , SignalR attend que le Task se termine, puis il renvoie le résultat non éjecté au client, de sorte qu’il n’y a aucune différence dans la façon dont vous codez l’appel de méthode dans le client.

Rendre une méthode Hub asynchrone évite de bloquer la connexion lorsqu’elle utilise le transport WebSocket. Lorsqu’une méthode Hub s’exécute de manière synchrone et que le transport est WebSocket, les appels de méthodes sur le hub à partir du même client sont bloqués jusqu’à ce que la méthode Hub se termine.

L’exemple suivant montre la même méthode codée pour s’exécuter de manière synchrone ou asynchrone, suivie du code client JavaScript qui fonctionne pour appeler l’une ou l’autre version.

Synchrone

public IEnumerable<Stock> GetAllStocks()
{
    // Returns data from memory.
    return _stockTicker.GetAllStocks();
}

Asynchrone - ASP.NET 4.5

public async Task<IEnumerable<Stock>> GetAllStocks()
{
    // Returns data from a web service.
    var uri = Util.getServiceUri("Stocks");
    using (HttpClient httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(uri);
        return (await response.Content.ReadAsAsync<IEnumerable<Stock>>());
    }
}

Client JavaScript utilisant le proxy généré

stockTickerHubProxy.server.getAllStocks().done(function (stocks) {
    $.each(stocks, function () {
        alert(this.Symbol + ' ' + this.Price);
    });
});

Pour plus d’informations sur l’utilisation de méthodes asynchrones dans ASP.NET 4.5, consultez Utilisation de méthodes asynchrones dans ASP.NET MVC 4.

Définition des surcharges

Si vous souhaitez définir des surcharges pour une méthode, le nombre de paramètres dans chaque surcharge doit être différent. Si vous différenciez une surcharge simplement en spécifiant différents types de paramètres, votre classe Hub est compilée, mais le service SignalR lève une exception au moment de l’exécution lorsque les clients essaient d’appeler l’une des surcharges.

Comment appeler des méthodes clientes à partir de la classe Hub

Pour appeler des méthodes clientes à partir du serveur, utilisez la Clients propriété dans une méthode de votre classe Hub. L’exemple suivant montre le code serveur qui appelle addNewMessageToPage sur tous les clients connectés et le code client qui définit la méthode dans un client JavaScript.

Serveur

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

Client JavaScript utilisant le proxy généré

contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + htmlEncode(name)
        + '</strong>: ' + htmlEncode(message) + '<li>');
};

Vous ne pouvez pas obtenir une valeur de retour à partir d’une méthode cliente ; syntaxe telle que int x = Clients.All.add(1,1) ne fonctionne pas.

Vous pouvez spécifier des types et des tableaux complexes pour les paramètres. L’exemple suivant passe un type complexe au client dans un paramètre de méthode.

Code serveur qui appelle une méthode cliente à l’aide d’un objet complexe

public void SendMessage(string name, string message)
{
    Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}

Code serveur qui définit l’objet complexe

public class ContosoChatMessage
{
    public string UserName { get; set; }
    public string Message { get; set; }
}

Client JavaScript utilisant le proxy généré

var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
    console.log(message.UserName + ' ' + message.Message);
});

Sélection des clients qui recevront le RPC

La propriété Clients renvoie un objet HubConnectionContext qui fournit plusieurs options pour spécifier les clients qui recevront le RPC :

  • Tous les clients connectés.

    Clients.All.addContosoChatMessageToPage(name, message);
    
  • Uniquement le client appelant.

    Clients.Caller.addContosoChatMessageToPage(name, message);
    
  • Tous les clients à l’exception du client appelant.

    Clients.Others.addContosoChatMessageToPage(name, message);
    
  • Un client spécifique identifié par l’ID de connexion.

    Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
    

    Cet exemple appelle addContosoChatMessageToPage sur le client appelant et a le même effet que l’utilisation de Clients.Caller.

  • Tous les clients connectés, à l’exception des clients spécifiés, identifiés par l’ID de connexion.

    Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Tous les clients connectés dans un groupe spécifié.

    Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Tous les clients connectés dans un groupe spécifié, à l’exception des clients spécifiés, identifiés par l’ID de connexion.

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Tous les clients connectés dans un groupe spécifié, à l’exception du client appelant.

    Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);
    

Aucune validation au moment de la compilation pour les noms de méthode

Le nom de la méthode que vous spécifiez est interprété comme un objet dynamique, ce qui signifie qu’il n’y a pas de validation IntelliSense ou de compilation pour celle-ci. L’expression est évaluée au moment de l’exécution. Lorsque l’appel de méthode s’exécute, SignalR envoie le nom de la méthode et les valeurs de paramètre au client, et si le client a une méthode qui correspond au nom, cette méthode est appelée et les valeurs de paramètre lui sont passées. Si aucune méthode correspondante n’est trouvée sur le client, aucune erreur n’est générée. Pour plus d’informations sur le format des données que SignalR transmet au client en arrière-plan lorsque vous appelez une méthode cliente, consultez Présentation de SignalR.

Correspondance de nom de méthode non sensible à la casse

La correspondance de nom de méthode ne respecte pas la casse. Par exemple, Clients.All.addContosoChatMessageToPage sur le serveur exécute AddContosoChatMessageToPage, addcontosochatmessagetopageou addContosoChatMessageToPage sur le client.

Exécution asynchrone

La méthode que vous appelez s’exécute de manière asynchrone. Tout code qui vient après un appel de méthode à un client s’exécute immédiatement sans attendre que SignalR termine la transmission des données aux clients, sauf si vous spécifiez que les lignes de code suivantes doivent attendre l’achèvement de la méthode. Les exemples de code suivants montrent comment exécuter deux méthodes clientes de manière séquentielle, l’une utilisant du code qui fonctionne dans .NET 4.5 et l’autre utilisant du code qui fonctionne dans .NET 4.

Exemple .NET 4.5

async public Task NewContosoChatMessage(string name, string message)
{
    await Clients.Others.addContosoChatMessageToPage(data);
    Clients.Caller.notifyMessageSent();
}

Exemple .NET 4

public void NewContosoChatMessage(string name, string message)
{
    (Clients.Others.addContosoChatMessageToPage(data) as Task).ContinueWith(antecedent =>
        Clients.Caller.notifyMessageSent());
}

Si vous utilisez await ou ContinueWith que vous attendez qu’une méthode cliente se termine avant l’exécution de la ligne de code suivante, cela ne signifie pas que les clients recevront réellement le message avant l’exécution de la ligne de code suivante. « Achèvement » d’un appel de méthode client signifie uniquement que SignalR a fait tout ce qui était nécessaire pour envoyer le message. Si vous avez besoin de vérifier que les clients ont reçu le message, vous devez programmer ce mécanisme vous-même. Par exemple, vous pouvez coder une MessageReceived méthode sur le hub, et dans la addContosoChatMessageToPage méthode sur le client, vous pouvez appeler MessageReceived après avoir fait le travail que vous devez effectuer sur le client. Dans MessageReceived le hub, vous pouvez faire tout ce qui dépend de la réception et du traitement réels du client de l’appel de méthode d’origine.

Comment utiliser une variable de chaîne comme nom de méthode

Si vous souhaitez appeler une méthode cliente à l’aide d’une variable de chaîne comme nom de la méthode, castez Clients.All (ou Clients.Others, Clients.Caller, etc.) en IClientProxy , puis appelez Invoke(nom_méthode, args...).

public void NewContosoChatMessage(string name, string message)
{
    string methodToCall = "addContosoChatMessageToPage";
    IClientProxy proxy = Clients.All;
    proxy.Invoke(methodToCall, name, message);
}

Comment gérer l’appartenance à un groupe à partir de la classe Hub

Les groupes dans SignalR fournissent une méthode pour diffuser des messages vers des sous-ensembles spécifiés de clients connectés. Un groupe peut avoir n’importe quel nombre de clients, et un client peut être membre d’un nombre quelconque de groupes.

Pour gérer l’appartenance au groupe, utilisez les méthodes Add et Remove fournies par la Groups propriété de la classe Hub. L’exemple suivant montre les méthodes et Groups.Remove utilisées dans les Groups.Add méthodes Hub appelées par le code client, suivies du code client JavaScript qui les appelle.

Serveur

public class ContosoChatHub : Hub
{
    public Task JoinGroup(string groupName)
    {
        return Groups.Add(Context.ConnectionId, groupName);
    }

    public Task LeaveGroup(string groupName)
    {
        return Groups.Remove(Context.ConnectionId, groupName);
    }
}

Client JavaScript utilisant le proxy généré

contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);

Vous n’avez pas besoin de créer explicitement des groupes. En effet, un groupe est créé automatiquement la première fois que vous spécifiez son nom dans un appel à Groups.Add, et il est supprimé lorsque vous supprimez la dernière connexion de son appartenance.

Il n’existe aucune API permettant d’obtenir une liste d’appartenances à un groupe ou une liste de groupes. SignalR envoie des messages aux clients et aux groupes en fonction d’un modèle pub/sub, et le serveur ne gère pas de listes de groupes ou d’appartenances à un groupe. Cela permet d’optimiser la scalabilité, car chaque fois que vous ajoutez un nœud à une batterie de serveurs web, tout état que SignalR gère doit être propagé au nouveau nœud.

Exécution asynchrone des méthodes Add et Remove

Les Groups.Add méthodes et s’exécutent Groups.Remove de manière asynchrone. Si vous souhaitez ajouter un client à un groupe et envoyer immédiatement un message au client à l’aide du groupe, vous devez vous assurer que la Groups.Add méthode se termine en premier. Les exemples de code suivants montrent comment procéder, l’un en utilisant du code qui fonctionne dans .NET 4.5 et l’autre en utilisant du code qui fonctionne dans .NET 4

Exemple .NET 4.5

async public Task JoinGroup(string groupName)
{
    await Groups.Add(Context.ConnectionId, groupName);
    Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}

Exemple .NET 4

public void JoinGroup(string groupName)
{
    (Groups.Add(Context.ConnectionId, groupName) as Task).ContinueWith(antecedent =>
        Clients.Group(groupName).addContosoChatMessageToPage(Context.ConnectionId + " added to group"));
}

Persistance de l’appartenance au groupe

SignalR effectue le suivi des connexions, et non des utilisateurs. Par conséquent, si vous souhaitez qu’un utilisateur fasse partie du même groupe chaque fois qu’il établit une connexion, vous devez appeler Groups.Add chaque fois que l’utilisateur établit une nouvelle connexion.

Après une perte temporaire de connectivité, SignalR peut parfois restaurer la connexion automatiquement. Dans ce cas, SignalR restaure la même connexion et n’établit pas de nouvelle connexion, de sorte que l’appartenance au groupe du client est automatiquement restaurée. Cela est possible même lorsque l’arrêt temporaire est le résultat d’un redémarrage ou d’une défaillance du serveur, car l’état de connexion de chaque client, y compris les appartenances au groupe, est arrondi au client. Si un serveur tombe en panne et est remplacé par un nouveau serveur avant l’expiration de la connexion, un client peut se reconnecter automatiquement au nouveau serveur et se réinscrire dans les groupes dont il est membre.

Lorsqu’une connexion ne peut pas être restaurée automatiquement après une perte de connectivité, lorsque la connexion expire ou lorsque le client se déconnecte (par exemple, lorsqu’un navigateur accède à une nouvelle page), les appartenances aux groupes sont perdues. La prochaine connexion de l’utilisateur sera une nouvelle connexion. Pour conserver les appartenances à un groupe lorsque le même utilisateur établit une nouvelle connexion, votre application doit suivre les associations entre les utilisateurs et les groupes, et restaurer les appartenances aux groupes chaque fois qu’un utilisateur établit une nouvelle connexion.

Pour plus d’informations sur les connexions et les reconnexions, consultez Comment gérer les événements de durée de vie des connexions dans la classe Hub plus loin dans cette rubrique.

Groupes mono-utilisateurs

Les applications qui utilisent SignalR doivent généralement effectuer le suivi des associations entre les utilisateurs et les connexions afin de savoir quel utilisateur a envoyé un message et quel(s) utilisateur(s) doivent recevoir un message. Les groupes sont utilisés dans l’un des deux modèles couramment utilisés pour cela.

  • Groupes mono-utilisateurs.

    Vous pouvez spécifier le nom d’utilisateur comme nom de groupe et ajouter l’ID de connexion actuel au groupe chaque fois que l’utilisateur se connecte ou se reconnecte. Pour envoyer des messages à l’utilisateur que vous envoyez au groupe. L’inconvénient de cette méthode est que le groupe ne vous permet pas de savoir si l’utilisateur est en ligne ou hors connexion.

  • Suivre les associations entre les noms d’utilisateur et les ID de connexion.

    Vous pouvez stocker une association entre chaque nom d’utilisateur et un ou plusieurs ID de connexion dans un dictionnaire ou une base de données, et mettre à jour les données stockées chaque fois que l’utilisateur se connecte ou se déconnecte. Pour envoyer des messages à l’utilisateur, spécifiez les ID de connexion. L’inconvénient de cette méthode est qu’elle prend plus de mémoire.

Comment gérer les événements de durée de vie de connexion dans la classe Hub

Les raisons courantes de la gestion des événements de durée de vie de connexion sont de suivre si un utilisateur est connecté ou non, et de suivre l’association entre les noms d’utilisateurs et les ID de connexion. Pour exécuter votre propre code lorsque les clients se connectent ou se déconnectent, remplacez les OnConnectedméthodes virtuelles , OnDisconnectedet OnReconnected de la classe Hub, comme illustré dans l’exemple suivant.

public class ContosoChatHub : Hub
{
    public override Task OnConnected()
    {
        // Add your own code here.
        // For example: in a chat application, record the association between
        // the current connection ID and user name, and mark the user as online.
        // After the code in this method completes, the client is informed that
        // the connection is established; for example, in a JavaScript client,
        // the start().done callback is executed.
        return base.OnConnected();
    }

    public override Task OnDisconnected()
    {
        // Add your own code here.
        // For example: in a chat application, mark the user as offline, 
        // delete the association between the current connection id and user name.
        return base.OnDisconnected();
    }

    public override Task OnReconnected()
    {
        // Add your own code here.
        // For example: in a chat application, you might have marked the
        // user as offline after a period of inactivity; in that case 
        // mark the user as online again.
        return base.OnReconnected();
    }
}

Quand OnConnected, OnDisconnected et OnReconnected sont appelés

Chaque fois qu’un navigateur accède à une nouvelle page, une nouvelle connexion doit être établie, ce qui signifie que SignalR exécute la OnDisconnected méthode suivie de la OnConnected méthode . SignalR crée toujours un ID de connexion lorsqu’une nouvelle connexion est établie.

La OnReconnected méthode est appelée lorsqu’il y a eu une interruption temporaire de la connectivité à partir de laquelle SignalR peut récupérer automatiquement, par exemple lorsqu’un câble est temporairement déconnecté et reconnecté avant l’expiration de la connexion. La OnDisconnected méthode est appelée lorsque le client est déconnecté et que SignalR ne peut pas se reconnecter automatiquement, par exemple lorsqu’un navigateur accède à une nouvelle page. Par conséquent, une séquence d’événements possible pour un client donné est OnConnected, OnReconnected, OnDisconnected; ou OnConnected. OnDisconnected Vous ne verrez pas la séquence OnConnected, OnDisconnectedpour OnReconnected une connexion donnée.

La OnDisconnected méthode n’est pas appelée dans certains scénarios, par exemple lorsqu’un serveur tombe en panne ou que le domaine d’application est recyclé. Lorsqu’un autre serveur est mis en ligne ou que le domaine d’application termine son recyclage, certains clients peuvent être en mesure de se reconnecter et de déclencher l’événement OnReconnected .

Pour plus d’informations, consultez Présentation et gestion des événements de durée de vie de connexion dans SignalR.

État de l’appelant non renseigné

Les méthodes de gestionnaire d’événements de durée de vie de connexion sont appelées à partir du serveur, ce qui signifie que tout état que vous avez placé dans l’objet state sur le client ne sera pas renseigné dans la Caller propriété sur le serveur. Pour plus d’informations sur l’objet state et la Caller propriété, consultez Comment passer l’état entre les clients et la classe Hub plus loin dans cette rubrique.

Comment obtenir des informations sur le client à partir de la propriété Context

Pour obtenir des informations sur le client, utilisez la Context propriété de la classe Hub. La Context propriété renvoie un objet HubCallerContext qui fournit l’accès aux informations suivantes :

  • ID de connexion du client appelant.

    string connectionID = Context.ConnectionId;
    

    L’ID de connexion est un GUID attribué par SignalR (vous ne pouvez pas spécifier la valeur dans votre propre code). Il existe un ID de connexion pour chaque connexion, et le même ID de connexion est utilisé par tous les hubs si vous avez plusieurs hubs dans votre application.

  • Données d’en-tête HTTP.

    System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
    

    Vous pouvez également obtenir des en-têtes HTTP à partir de Context.Headers. La raison pour laquelle plusieurs références à la même chose ont Context.Headers été créées en premier, la Context.Request propriété a été ajoutée ultérieurement et Context.Headers a été conservée à des fins de compatibilité descendante.

  • Données de chaîne de requête.

    System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString;
    string parameterValue = queryString["parametername"]
    

    Vous pouvez également obtenir des données de chaîne de requête à partir de Context.QueryString.

    La chaîne de requête que vous obtenez dans cette propriété est celle qui a été utilisée avec la requête HTTP qui a établi la connexion SignalR. Vous pouvez ajouter des paramètres de chaîne de requête dans le client en configurant la connexion, ce qui est un moyen pratique de transmettre des données sur le client du client au serveur. L’exemple suivant montre une façon d’ajouter une chaîne de requête dans un client JavaScript lorsque vous utilisez le proxy généré.

    $.connection.hub.qs = { "version" : "1.0" };
    

    Pour plus d’informations sur la définition des paramètres de chaîne de requête, consultez les guides d’API pour les clients JavaScript et .NET .

    Vous trouverez la méthode de transport utilisée pour la connexion dans les données de chaîne de requête, ainsi que d’autres valeurs utilisées en interne par SignalR :

    string transportMethod = queryString["transport"];
    

    La valeur de transportMethod est « webSockets », « serverSentEvents », « foreverFrame » ou « longPolling ». Notez que si vous case activée cette valeur dans la OnConnected méthode de gestionnaire d’événements, dans certains scénarios, vous pouvez initialement obtenir une valeur de transport qui n’est pas la méthode de transport négociée finale pour la connexion. Dans ce cas, la méthode lève une exception et est appelée ultérieurement lorsque la méthode de transport finale est établie.

  • Cookies.

    System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
    

    Vous pouvez également obtenir des cookies à partir de Context.RequestCookies.

  • informations utilisateur.

    System.Security.Principal.IPrincipal user = Context.User;
    
  • Objet HttpContext pour la requête :

    System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
    

    Utilisez cette méthode au lieu d’obtenir HttpContext.Current l’objet HttpContext pour la connexion SignalR.

Comment passer l’état entre les clients et la classe Hub

Le proxy client fournit un state objet dans lequel vous pouvez stocker les données que vous souhaitez transmettre au serveur à chaque appel de méthode. Sur le serveur, vous pouvez accéder à ces données dans la propriété dans les Clients.Caller méthodes Hub appelées par les clients. La Clients.Caller propriété n’est pas renseignée pour les méthodes OnConnectedde gestionnaire d’événements de durée de vie de connexion , OnDisconnectedet OnReconnected.

La création ou la mise à jour de données dans l’objet state et la Clients.Caller propriété fonctionne dans les deux sens. Vous pouvez mettre à jour les valeurs dans le serveur et elles sont renvoyées au client.

L’exemple suivant montre le code client JavaScript qui stocke l’état pour la transmission au serveur à chaque appel de méthode.

contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";

L’exemple suivant montre le code équivalent dans un client .NET.

contosoChatHubProxy["userName"] = "Fadi Fakhouri";
chatHubProxy["computerName"] = "fadivm1";

Dans votre classe Hub, vous pouvez accéder à ces données dans la Clients.Caller propriété . L’exemple suivant montre le code qui récupère l’état auquel il est fait référence dans l’exemple précédent.

public void NewContosoChatMessage(string data)
{
    string userName = Clients.Caller.userName;
    string computerName = Clients.Caller.computerName;
    Clients.Others.addContosoChatMessageToPage(message, userName, computerName);
}

Notes

Ce mécanisme de persistance de l’état n’est pas destiné à de grandes quantités de données, car tout ce que vous placez dans la state propriété ou Clients.Caller est aller-retour avec chaque appel de méthode. Il est utile pour les éléments plus petits tels que les noms d’utilisateur ou les compteurs.

Comment gérer les erreurs dans la classe Hub

Pour gérer les erreurs qui se produisent dans vos méthodes de classe Hub, utilisez l’une des méthodes suivantes ou les deux :

  • Encapsulez votre code de méthode dans des blocs try-catch et journalisez l’objet exception. À des fins de débogage, vous pouvez envoyer l’exception au client, mais pour des raisons de sécurité, l’envoi d’informations détaillées aux clients en production n’est pas recommandé.

  • Créez un module de pipeline Hubs qui gère la méthode OnIncomingError . L’exemple suivant montre un module de pipeline qui journalise les erreurs, suivi d’un code dans Global.asax qui injecte le module dans le pipeline Hubs.

    public class ErrorHandlingPipelineModule : HubPipelineModule
    {
        protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context)
        {
            Debug.WriteLine("=> Exception " + ex.Message);
            if (ex.InnerException != null)
            {
                Debug.WriteLine("=> Inner Exception " + ex.InnerException.Message);
            }
            base.OnIncomingError(ex, context);
        }
    }
    
    protected void Application_Start(object sender, EventArgs e)
    {
        GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); 
        RouteTable.Routes.MapHubs();
    }
    

Pour plus d’informations sur les modules de pipeline Hub, consultez Comment personnaliser le pipeline Hubs plus loin dans cette rubrique.

Comment activer le suivi

Pour activer le suivi côté serveur, ajoutez un système. diagnostics élément à votre fichier Web.config, comme illustré dans cet exemple :

<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit https://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="SignalRSamples" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;" />
    <add name="SignalRSamplesWithMARS" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;MultipleActiveResultSets=true;" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
  <system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
     <source name="SignalR.ServiceBusMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
     </source>
     <source name="SignalR.ScaleoutMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.Transports.WebSocketTransport">
        <listeners>
          <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.ServerSentEventsTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.ForeverFrameTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.LongPollingTransport">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.TransportHeartBeat">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
    </switches>
    <sharedListeners>
      <add name="SignalR-Transports" 
           type="System.Diagnostics.TextWriterTraceListener" 
           initializeData="transports.log.txt" />
        <add name="SignalR-Bus"
           type="System.Diagnostics.TextWriterTraceListener"
           initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
  </entityFramework>
</configuration>

Lorsque vous exécutez l’application dans Visual Studio, vous pouvez afficher les journaux dans la fenêtre Sortie .

Comment appeler des méthodes clientes et gérer des groupes en dehors de la classe Hub

Pour appeler des méthodes clientes à partir d’une classe différente de celle de votre classe Hub, obtenez une référence à l’objet de contexte SignalR pour le hub et utilisez-le pour appeler des méthodes sur le client ou gérer des groupes.

L’exemple StockTicker de classe suivant obtient l’objet de contexte, le stocke dans un instance de la classe, stocke la classe instance dans une propriété statique et utilise le contexte de la classe singleton instance pour appeler la méthode sur les updateStockPrice clients connectés à un hub nommé StockTickerHub.

// For the complete example, go to 
// http://www.asp.net/signalr/overview/signalr-1x/getting-started/tutorial-server-broadcast-with-aspnet-signalr
// This sample only shows code related to getting and using the SignalR context.
public class StockTicker
{
    // Singleton instance
    private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
        () => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>()));

    private IHubContext _context;

    private StockTicker(IHubContext context)
    {
        _context = context;
    }

    // This method is invoked by a Timer object.
    private void UpdateStockPrices(object state)
    {
        foreach (var stock in _stocks.Values)
        {
            if (TryUpdateStockPrice(stock))
            {
                _context.Clients.All.updateStockPrice(stock);
            }
        }
    }

Si vous devez utiliser le contexte plusieurs fois dans un objet de longue durée, obtenez la référence une fois et enregistrez-la plutôt que de la récupérer à chaque fois. L’obtention du contexte une fois garantit que SignalR envoie des messages aux clients dans la même séquence dans laquelle vos méthodes Hub effectuent des appels de méthode client. Pour obtenir un tutoriel qui montre comment utiliser le contexte SignalR pour un hub, consultez Diffusion de serveur avec ASP.NET SignalR.

Appel de méthodes clientes

Vous pouvez spécifier les clients qui recevront le RPC, mais vous avez moins d’options que lorsque vous appelez à partir d’une classe Hub. La raison en est que le contexte n’est pas associé à un appel particulier d’un client, de sorte que toutes les méthodes qui nécessitent une connaissance de l’ID de connexion actuel, telles que Clients.Others, ou Clients.Callerou , Clients.OthersInGroupne sont pas disponibles. Les options suivantes sont disponibles :

  • Tous les clients connectés.

    context.Clients.All.addContosoChatMessageToPage(name, message);
    
  • Un client spécifique identifié par l’ID de connexion.

    context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
    
  • Tous les clients connectés, à l’exception des clients spécifiés, identifiés par l’ID de connexion.

    context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Tous les clients connectés dans un groupe spécifié.

    context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Tous les clients connectés dans un groupe spécifié, à l’exception des clients spécifiés, identifiés par l’ID de connexion.

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    

Si vous appelez votre classe non Hub à partir de méthodes de votre classe Hub, vous pouvez passer l’ID de connexion actuel et l’utiliser avec Clients.Client, Clients.AllExceptou Clients.Group pour simuler Clients.Caller, Clients.Othersou Clients.OthersInGroup. Dans l’exemple suivant, la MoveShapeHub classe transmet l’ID de connexion à la Broadcaster classe afin que la Broadcaster classe puisse simuler Clients.Others.

// For the complete example, see
// http://www.asp.net/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr
// This sample only shows code that passes connection ID to the non-Hub class,
// in order to simulate Clients.Others.
public class MoveShapeHub : Hub
{
    // Code not shown puts a singleton instance of Broadcaster in this variable.
    private Broadcaster _broadcaster;

    public void UpdateModel(ShapeModel clientModel)
    {
        clientModel.LastUpdatedBy = Context.ConnectionId;
        // Update the shape model within our broadcaster
        _broadcaster.UpdateShape(clientModel);
    }
}

public class Broadcaster
{
    public Broadcaster()
    {
        _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
    }

    public void UpdateShape(ShapeModel clientModel)
    {
        _model = clientModel;
        _modelUpdated = true;
    }

    // Called by a Timer object.
    public void BroadcastShape(object state)
    {
        if (_modelUpdated)
        {
            _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
            _modelUpdated = false;
        }
    }
}

Gestion de l’appartenance aux groupes

Pour la gestion des groupes, vous disposez des mêmes options que dans une classe Hub.

  • Ajouter un client à un groupe

    context.Groups.Add(connectionID, groupName);
    
  • Supprimer un client d’un groupe

    context.Groups.Remove(connectionID, groupName);
    

Comment personnaliser le pipeline Hubs

SignalR vous permet d’injecter votre propre code dans le pipeline Hub. L’exemple suivant montre un module de pipeline Hub personnalisé qui journalise chaque appel de méthode entrant reçu du client et l’appel de méthode sortant appelé sur le client :

public class LoggingPipelineModule : HubPipelineModule 
{ 
    protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) 
    { 
        Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name); 
        return base.OnBeforeIncoming(context); 
    }   
    protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) 
    { 
        Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub); 
        return base.OnBeforeOutgoing(context); 
    } 
}

Le code suivant dans le fichier Global.asax inscrit le module à exécuter dans le pipeline Hub :

protected void Application_Start(object sender, EventArgs e) 
{ 
    GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); 
    RouteTable.Routes.MapHubs();
}

Il existe de nombreuses méthodes différentes que vous pouvez remplacer. Pour obtenir la liste complète, consultez Méthodes HubPipelineModule.