SignalR - Invio di un messaggio a un utente specifico utilizzando (IUserIdProvider) * NEW 2.0.0 *


101

Nell'ultima versione di Asp.Net SignalR, è stata aggiunta una nuova modalità di invio di un messaggio ad un utente specifico, utilizzando l'interfaccia "IUserIdProvider".

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

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

La mia domanda è: come faccio a sapere a chi sto inviando il mio messaggio? La spiegazione di questo nuovo metodo è molto superficiale. E la bozza della dichiarazione di SignalR 2.0.0 con questo bug e non si compila. Qualcuno ha implementato questa funzione?

Ulteriori informazioni: http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider

Abbracci.


1
È necessario esaminare l'autenticazione e l'autorizzazione con SignalR. UserId farà parte del provider IPrincipal.
Gjohn

Risposte:


153

SignalR fornisce ConnectionId per ogni connessione. Per trovare quale connessione appartiene a chi (l'utente), dobbiamo creare una mappatura tra la connessione e l'utente. Dipende da come identifichi un utente nella tua applicazione.

In SignalR 2.0, questo viene fatto utilizzando l'inbuilt IPrincipal.Identity.Name, che è l'identificatore dell'utente connesso come impostato durante l'autenticazione ASP.NET.

Tuttavia, potrebbe essere necessario mappare la connessione con l'utente utilizzando un identificatore diverso invece di utilizzare Identity.Name. A tale scopo, questo nuovo provider può essere utilizzato con l'implementazione personalizzata per mappare l'utente con la connessione.

Esempio di mappatura degli utenti SignalR alle connessioni tramite IUserIdProvider

Supponiamo che la nostra applicazione utilizzi un userIdper identificare ogni utente. Ora dobbiamo inviare un messaggio a un utente specifico. Abbiamo userIde message, ma SignalR deve anche conoscere la mappatura tra il nostro ID utente e la connessione.

Per ottenere ciò, prima dobbiamo creare una nuova classe che implementa 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();
    }
}

Il secondo passo è dire a SignalR di usare il nostro CustomUserIdProviderinvece dell'implementazione predefinita. Questa operazione può essere eseguita in Startup.cs durante l'inizializzazione della configurazione dell'hub:

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();
    }
}

Ora puoi inviare un messaggio a un utente specifico usando il suo userIdcome menzionato nella documentazione, come:

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

Spero che questo ti aiuti.


Ciao amico, scusa per il feedback in ritardo! Ma ho provato ad accedere all'ID che genera CustomUserIdProvider ma il metodo "OnConnected" non è lo stesso. Come mi collego a un utente nel database? Grazie!
Igor

7
Cos'è MyCustomUserClass?
Danny Bullis

2
"MyCustomUserClass" potrebbe essere la tua classe utente personalizzata che contiene il metodo FindUserId. Questo è solo per esempio. Potresti avere qualsiasi metodo in qualsiasi classe che restituisca UserId e usarlo qui.
Sumant

5
Grazie @Sumant, il mio problema è finito per essere che b / c ero in un progetto API Web in cui avevo implementato OAuth 2 con token bearer ho dovuto implementare la logica per passare il token bearer sulla stringa di query poiché non può essere estratto da le intestazioni su quella richiesta di connessione del segnale iniziale. Non potevo semplicemente usare request.User.Identity.Name
xinunix

1
@Sumant l'ho già risolto. Il problema era che ho inserito app.MapSignalR();global.asax prima dell'autenticazione.
Mr. Robot,

38

Ecco un inizio .. Aperto a suggerimenti / miglioramenti.

server

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("2@2.com").addChatMessage(name, message);
    }

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

        return base.OnConnected();
    }
}

JavaScript

(Nota come addChatMessagee sendChatMessagesono anche metodi nel codice del server sopra)

    $(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();
        });
    });
});

Test inserisci qui la descrizione dell'immagine


Ciao amico, scusa per il feedback in ritardo! Ma come posso evitare la perdita di CONNECTIONID? Grazie.
Igor

5
@lgao non ne ho idea.
The Muffin Man

perché questa riga era richiesta --- Clients.Group ("2@2.com"). addChatMessage (name, message); ??
Thomas

@Thomas Probabilmente l'ho incluso per il bene della demo. Deve esserci un altro modo per trasmettere a un gruppo specifico poiché era codificato.
The Muffin Man

Questa semplice soluzione ha risolto il mio problema di inviare un messaggio a uno specifico utente registrato. È semplice, veloce e facile da capire. Voterei questa risposta più volte se potessi.
Rafael AMS

5

Ecco come utilizzare SignarR per indirizzare un utente specifico (senza utilizzare alcun provider):

 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!");

2
come stai usando questo non l' contextIdClientho capito :(
Neo

2

Guarda i test di SignalR per la funzionalità.

Il test "SendToUser" prende automaticamente l'identità dell'utente passata utilizzando una normale libreria di autenticazione owin.

Lo scenario è che hai un utente che si è connesso da più dispositivi / browser e vuoi inviare un messaggio a tutte le sue connessioni attive.


Grazie uomo! Ma la versione del progetto 2.0 non sta compilando SignalR qui. : (. Purtroppo non posso accedervi.
Igor

0

Vecchio thread, ma mi sono imbattuto in questo in un campione:

services.AddSignalR()
            .AddAzureSignalR(options =>
        {
            options.ClaimsProvider = context => new[]
            {
                new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
            };
        });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.