SignalR - Senden einer Nachricht an einen bestimmten Benutzer mit (IUserIdProvider) * NEW 2.0.0 *

101

In der neuesten Version von Asp.Net SignalR wurde eine neue Methode zum Senden einer Nachricht an einen bestimmten Benutzer über die Schnittstelle "IUserIdProvider" hinzugefügt.

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

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

Meine Frage lautet: Woher weiß ich, an wen ich meine Nachricht sende? Die Erklärung dieser neuen Methode ist sehr oberflächlich. Und der Entwurf der Anweisung von SignalR 2.0.0 mit diesem Fehler und nicht kompiliert. Hat jemand diese Funktion implementiert?

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

Umarmungen.

Igor
quelle
1
Sie müssen sich mit Authentifizierung und Autorisierung mit SignalR befassen. Die UserId wird Teil des IPrincipal-Anbieters.
Gjohn

Antworten:

152

SignalR stellt für jede Verbindung die Verbindungs-ID bereit. Um herauszufinden, welche Verbindung zu wem (dem Benutzer) gehört, müssen wir eine Zuordnung zwischen der Verbindung und dem Benutzer erstellen. Dies hängt davon ab, wie Sie einen Benutzer in Ihrer Anwendung identifizieren.

In SignalR 2.0 erfolgt dies mithilfe der eingebauten IPrincipal.Identity.NameBenutzerkennung, die während der ASP.NET-Authentifizierung festgelegt wurde.

Möglicherweise müssen Sie jedoch die Verbindung mit dem Benutzer mithilfe eines anderen Bezeichners zuordnen, anstatt den Identity.Name zu verwenden. Zu diesem Zweck kann dieser neue Anbieter mit Ihrer benutzerdefinierten Implementierung zum Zuordnen von Benutzern zur Verbindung verwendet werden.

Beispiel für die Zuordnung von SignalR-Benutzern zu Verbindungen mithilfe von IUserIdProvider

Nehmen wir an, unsere Anwendung verwendet a userId, um jeden Benutzer zu identifizieren. Jetzt müssen wir eine Nachricht an einen bestimmten Benutzer senden. Wir haben userIdund message, aber SignalR muss auch die Zuordnung zwischen unserer Benutzer-ID und der Verbindung kennen.

Um dies zu erreichen, müssen wir zuerst eine neue Klasse erstellen, die Folgendes implementiert 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();
    }
}

Der zweite Schritt besteht darin, SignalR anzuweisen, unsere CustomUserIdProvideranstelle der Standardimplementierung zu verwenden. Dies kann in der Datei Startup.cs erfolgen, während die Hub-Konfiguration initialisiert wird:

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

Jetzt können Sie eine Nachricht an einen bestimmten Benutzer senden, indem Sie dessen userIdwie in der Dokumentation beschrieben verwenden, z.

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

Hoffe das hilft.

Sumant
quelle
Hallo Freund, entschuldige das späte Feedback! Ich habe jedoch versucht, auf die ID zuzugreifen, die CustomUserIdProvider generiert, aber die Methode "OnConnected" ist nicht dieselbe. Wie verlinke ich einen Benutzer in der Datenbank? Danke dir!
Igor
7
Was ist MyCustomUserClass?
Danny Bullis
2
"MyCustomUserClass" kann Ihre benutzerdefinierte Benutzerklasse sein, die die FindUserId-Methode enthält. Dies ist nur zum Beispiel. Sie können jede Methode in jeder Klasse haben, die Ihre Benutzer-ID zurückgibt, und diese hier verwenden.
Sumant
5
Dank @Sumant war mein Problem, dass ich mich in einem Web-API-Projekt befand, in dem ich OAuth 2 mit Bearer-Token implementiert hatte. Ich musste Logik implementieren, um das Bearer-Token an die Abfragezeichenfolge zu übergeben, da es nicht abgerufen werden kann Die Header dieser anfänglichen Signal- oder Verbindungsanforderung. Konnte nicht einfach request.User.Identity.Name
xinunix
1
@Sumant das habe ich schon gelöst. Das Problem war, dass ich app.MapSignalR();global.asax vor der Authentifizierung eingegeben habe.
Mr. Robot
38

Hier ist ein Anfang. Offen für Vorschläge / Verbesserungen.

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("[email protected]").addChatMessage(name, message);
    }

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

        return base.OnConnected();
    }
}

JavaScript

(Beachten Sie, wie addChatMessageund sendChatMessagesind auch Methoden im obigen Servercode)

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

Testen Geben Sie hier die Bildbeschreibung ein

Der Muffin-Mann
quelle
Hallo Freund, entschuldige das späte Feedback! Aber wie kann ich den Verlust von CONNECTIONID verhindern? Danke dir.
Igor
5
@ lgao Ich habe keine Ahnung.
Der Muffin-Mann
warum diese Zeile erforderlich war --- Clients.Group ("[email protected]"). addChatMessage (Name, Nachricht); ??
Thomas
@ Thomas Ich habe es wahrscheinlich für die Demo aufgenommen. Es muss eine andere Möglichkeit geben, an eine bestimmte Gruppe zu senden, da diese fest codiert wurde.
Der Muffin-Mann
Diese einfache Lösung löste mein Problem, eine Nachricht an einen bestimmten protokollierten Benutzer zu senden. Es ist einfach, schnell und leicht zu verstehen. Ich würde diese Antwort mehrmals positiv bewerten, wenn ich könnte.
Rafael AMS
5

So verwenden Sie SignarR, um einen bestimmten Benutzer anzusprechen (ohne einen Anbieter zu verwenden):

 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
quelle
2
Wie Sie dies contextIdClientverwenden, haben Sie nicht verstanden :(
Neo
2

Schauen Sie sich SignalR-Tests für die Funktion an.

Test "SendToUser" verwendet automatisch die Benutzeridentität, die mithilfe einer regulären Owin-Authentifizierungsbibliothek übergeben wurde.

Das Szenario besteht darin, dass Sie einen Benutzer haben, der über mehrere Geräte / Browser eine Verbindung hergestellt hat, und eine Nachricht an alle seine aktiven Verbindungen senden möchten.

Gustavo Armenta
quelle
Danke, Mann! In der Projektversion 2.0 wird SignalR hier jedoch nicht kompiliert. : (. Leider kann ich nicht darauf zugreifen.
Igor
0

Alter Thread, aber gerade in einem Beispiel darauf gestoßen:

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