SignalR - Envoi d'un message à un utilisateur spécifique en utilisant (IUserIdProvider) * NOUVEAU 2.0.0 *

101

Dans la dernière version d'Asp.Net SignalR, a été ajoutée une nouvelle façon d'envoyer un message à un utilisateur spécifique, en utilisant l'interface "IUserIdProvider".

public interface IUserIdProvider
{
   string GetUserId(IRequest request);
}

public class MyHub : Hub
{
   public void Send(string userId, string message)
   {
      Clients.User(userId).send(message);
   }
}

Ma question est la suivante: comment savoir à qui j'envoie mon message? L'explication de cette nouvelle méthode est très superficielle. Et le projet de déclaration de SignalR 2.0.0 avec ce bogue et ne compile pas. Quelqu'un a-t-il implémenté cette fonctionnalité?

Plus d'informations: http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider

Câlins.

Igor
la source
1
Vous devez examiner l'authentification et l'autorisation avec SignalR. L'UserId fera partie du fournisseur IPrincipal.
Gjohn

Réponses:

153

SignalR fournit ConnectionId pour chaque connexion. Pour trouver quelle connexion appartient à qui (l'utilisateur), nous devons créer un mappage entre la connexion et l'utilisateur. Cela dépend de la manière dont vous identifiez un utilisateur dans votre application.

Dans SignalR 2.0, cela se fait à l'aide de la fonction intégrée IPrincipal.Identity.Name, qui est l'identificateur de l'utilisateur connecté tel que défini lors de l'authentification ASP.NET.

Cependant, vous devrez peut-être mapper la connexion avec l'utilisateur à l'aide d'un identificateur différent au lieu d'utiliser Identity.Name. À cette fin, ce nouveau fournisseur peut être utilisé avec votre implémentation personnalisée pour mapper l'utilisateur avec la connexion.

Exemple de mappage d'utilisateurs SignalR à des connexions à l'aide de IUserIdProvider

Supposons que notre application utilise un userIdpour identifier chaque utilisateur. Maintenant, nous devons envoyer un message à un utilisateur spécifique. Nous avons userIdet message, mais SignalR doit également connaître le mappage entre notre userId et la connexion.

Pour y parvenir, nous devons d'abord créer une nouvelle classe qui implémente IUserIdProvider:

public class CustomUserIdProvider : IUserIdProvider
{
     public string GetUserId(IRequest request)
    {
        // your logic to fetch a user identifier goes here.

        // for example:

        var userId = MyCustomUserClass.FindUserId(request.User.Identity.Name);
        return userId.ToString();
    }
}

La deuxième étape consiste à indiquer à SignalR d'utiliser notre CustomUserIdProviderau lieu de l'implémentation par défaut. Cela peut être fait dans Startup.cs lors de l'initialisation de la configuration du concentrateur:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var idProvider = new CustomUserIdProvider();

        GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);          

        // Any connection or hub wire up and configuration should go here
        app.MapSignalR();
    }
}

Maintenant, vous pouvez envoyer un message à un utilisateur spécifique en utilisant le sien userIdcomme mentionné dans la documentation, comme:

public class MyHub : Hub
{
   public void Send(string userId, string message)
   {
      Clients.User(userId).send(message);
   }
}

J'espère que cela t'aides.

Sumant
la source
Salut ami, désolé pour les commentaires tardifs! Mais j'ai essayé d'accéder à l'Id qui génère CustomUserIdProvider mais la méthode "OnConnected" n'est pas la même. Comment créer un lien vers un utilisateur dans la base de données? Je vous remercie!
Igor
7
Qu'est-ce que MyCustomUserClass?
Danny Bullis
2
"MyCustomUserClass" peut être votre classe d'utilisateur personnalisée qui contient la méthode FindUserId. Ceci est juste par exemple. Vous pouvez avoir n'importe quelle méthode dans n'importe quelle classe qui renvoie votre UserId et l'utiliser ici.
Sumant
5
Merci @Sumant, mon problème a fini par être que b / c j'étais dans un projet d'API Web où j'avais implémenté OAuth 2 avec un jeton de support, je devais implémenter une logique pour transmettre le jeton de support sur la chaîne de requête car il ne peut pas être extrait les en-têtes de cette demande initiale de connexion de signal. Impossible d'utiliser simplement request.User.Identity.Name
xinunix
1
@Sumant J'ai déjà résolu cela. Le problème était que j'avais mis app.MapSignalR();global.asax avant l'authentification.
Mr. Robot
38

Voici un début .. Ouvert aux suggestions / améliorations.

Serveur

public class ChatHub : Hub
{
    public void SendChatMessage(string who, string message)
    {
        string name = Context.User.Identity.Name;
        Clients.Group(name).addChatMessage(name, message);
        Clients.Group("[email protected]").addChatMessage(name, message);
    }

    public override Task OnConnected()
    {
        string name = Context.User.Identity.Name;
        Groups.Add(Context.ConnectionId, name);

        return base.OnConnected();
    }
}

JavaScript

(Notez comment addChatMessageet sendChatMessagesont également des méthodes dans le code serveur ci-dessus)

    $(function () {
    // Declare a proxy to reference the hub.
    var chat = $.connection.chatHub;
    // Create a function that the hub can call to broadcast messages.
    chat.client.addChatMessage = function (who, message) {
        // Html encode display name and message.
        var encodedName = $('<div />').text(who).html();
        var encodedMsg = $('<div />').text(message).html();
        // Add the message to the page.
        $('#chat').append('<li><strong>' + encodedName
            + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
    };

    // Start the connection.
    $.connection.hub.start().done(function () {
        $('#sendmessage').click(function () {
            // Call the Send method on the hub.
            chat.server.sendChatMessage($('#displayname').val(), $('#message').val());
            // Clear text box and reset focus for next comment.
            $('#message').val('').focus();
        });
    });
});

Essai entrez la description de l'image ici

L'homme aux muffins
la source
Salut ami, désolé pour les commentaires tardifs! Mais comment puis-je éviter la perte de CONNECTIONID? Je vous remercie.
Igor
5
@lgao Je n'ai aucune idée.
The Muffin Man
pourquoi cette ligne était requise --- Clients.Group ("[email protected]"). addChatMessage (nom, message); ??
Thomas
@Thomas Je l'ai probablement inclus pour le plaisir de la démo. Il doit y avoir une autre façon de diffuser à un groupe spécifique car cela a été codé en dur.
The Muffin Man
Cette solution simple a résolu mon problème d'envoyer un message à un utilisateur connecté spécifique. C'est simple, rapide et facile à comprendre. Je voterais cette réponse plusieurs fois si je le pouvais.
Rafael AMS
5

Voici comment utiliser SignarR pour cibler un utilisateur spécifique (sans utiliser de fournisseur):

 private static ConcurrentDictionary<string, string> clients = new ConcurrentDictionary<string, string>();

 public string Login(string username)
 {
     clients.TryAdd(Context.ConnectionId, username);            
     return username;
 }

// The variable 'contextIdClient' is equal to Context.ConnectionId of the user, 
// once logged in. You have to store that 'id' inside a dictionaty for example.  
Clients.Client(contextIdClient).send("Hello!");
Matteo Gariglio
la source
2
comment vous l'utilisez ne l'a contextIdClientpas compris :(
Neo
2

Regardez les tests SignalR pour la fonctionnalité.

Le test "SendToUser" prend automatiquement l'identité de l'utilisateur transmise à l'aide d'une bibliothèque d'authentification owin standard.

Le scénario est que vous avez un utilisateur qui s'est connecté à partir de plusieurs appareils / navigateurs et que vous souhaitez envoyer un message à toutes ses connexions actives.

Gustavo Armenta
la source
Merci mec! Mais la version 2.0 du projet ne compile pas SignalR ici. : (. Malheureusement, je ne peux pas y accéder.
Igor
0

Ancien fil, mais je suis juste tombé sur ceci dans un exemple:

services.AddSignalR()
            .AddAzureSignalR(options =>
        {
            options.ClaimsProvider = context => new[]
            {
                new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
            };
        });
Greg Gum
la source