SignalR - 使用 (IUserIdProvider) *NEW 2.0.0* 向特定用户发送消息

Posted

技术标签:

【中文标题】SignalR - 使用 (IUserIdProvider) *NEW 2.0.0* 向特定用户发送消息【英文标题】:SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0* 【发布时间】:2013-10-31 13:45:33 【问题描述】:

在最新版本的 Asp.Net SignalR 中,添加了一种使用接口“IUserIdProvider”向特定用户发送消息的新方法。

public interface IUserIdProvider

   string GetUserId(IRequest request);


public class MyHub : Hub

   public void Send(string userId, string message)
   
      Clients.User(userId).send(message);
   

我的问题是:我怎么知道我要向谁发送消息?这种新方法的解释很肤浅。 SignalR 2.0.0 的声明草案存在此错误且无法编译。有人实现了这个功能吗?

更多信息:http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider

拥抱。

【问题讨论】:

您需要查看使用 SignalR 的身份验证和授权。 UserId 将成为 IPrincipal 提供者的一部分。 【参考方案1】:

SignalR 为每个连接提供 ConnectionId。要找到哪个连接属于谁(用户),我们需要在连接和用户之间创建一个映射。这取决于您如何在应用程序中识别用户。

在 SignalR 2.0 中,这是通过使用内置的 IPrincipal.Identity.Name 来完成的,这是在 ASP.NET 身份验证期间设置的登录用户标识符。

但是,您可能需要使用不同的标识符而不是使用 Identity.Name 来映射与用户的连接。为此,此新提供程序可以与您的自定义实现一起使用,以将用户映射到连接。

使用 IUserIdProvider 将 SignalR 用户映射到连接的示例

假设我们的应用程序使用userId 来识别每个用户。现在,我们需要向特定用户发送消息。我们有 userIdmessage,但 SignalR 还必须知道我们的 userId 和连接之间的映射。

为此,首先我们需要创建一个实现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();
    

第二步是告诉 SignalR 使用我们的CustomUserIdProvider 而不是默认实现。这可以在初始化集线器配置时在 Startup.cs 中完成:

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

现在,您可以使用文档中提到的userId 向特定用户发送消息,例如:

public class MyHub : Hub

   public void Send(string userId, string message)
   
      Clients.User(userId).send(message);
   

希望这会有所帮助。

【讨论】:

什么是 MyCustomUserClass? "MyCustomUserClass" 可以是包含 FindUserId 方法的自定义用户类。这只是一个例子。您可以在任何类中拥有返回 UserId 并在此处使用的任何方法。 谢谢@Sumant,我的问题最终是 b/c 我在一个 Web API 项目中,我用不记名令牌实现了 OAuth 2 我必须实现逻辑以在查询字符串上传递不记名令牌因为无法从初始信号器连接请求的标头中提取它。不能只使用 request.User.Identity.Name @xinunix 在 Web API 项目中,您仍然可以使用HttpContext.Current.User.Identity.Name。你试过了吗? @Sumant 我已经解决了。问题是我在身份验证之前将app.MapSignalR(); 放在 global.asax 中。【参考方案2】:

这是一个开始。欢迎提出建议/改进。

服务器

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

(注意addChatMessagesendChatMessage 也是上面服务器代码中的方法)

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

测试

【讨论】:

您好朋友,很抱歉反馈迟了!但是如何防止丢失 CONNECTIONID?谢谢。 @lgao 我不知道。 为什么需要这一行 --- Clients.Group("2@2.com").addChatMessage(name, message); ?? @Thomas 我可能为了演示而将其包括在内。必须有另一种方式向特定组广播,因为这是硬编码的。 这个简单的解决方案解决了我向特定登录用户发送消息的问题。它简单、快速且易于理解。如果可以的话,我会多次支持这个答案。【参考方案3】:

这是使用 SignarR 来定位特定用户的方式(不使用任何提供者):

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

【讨论】:

你是如何使用这个contextIdClient没明白:(【参考方案4】:

查看SignalR Tests 的功能。

Test "SendToUser" 自动获取使用常规 owin 身份验证库传递的用户身份。

场景是您有一个从多个设备/浏览器进行连接的用户,您希望将消息推送到他的所有活动连接。

【讨论】:

谢谢你!但是项目版本 2.0 并没有在这里编译 SignalR。 : (。不幸的是我无法访问它。【参考方案5】:

旧线程,但只是在示例中遇到了这个:

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

【讨论】:

【参考方案6】:

对于任何试图在 asp.net 核心中执行此操作的人。您可以使用声明。

public class CustomEmailProvider : IUserIdProvider

    public virtual string GetUserId(HubConnectionContext connection)
    
        return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
    

可以使用任何标识符,但它必须是唯一的。例如,如果您使用名称标识符,这意味着如果有多个用户与收件人同名,则消息也会发送给他们。我选择电子邮件是因为它对每个用户都是独一无二的。

然后在启动类中注册服务。

services.AddSingleton<IUserIdProvider, CustomEmailProvider>();

接下来。在用户注册期间添加声明。

var result = await _userManager.CreateAsync(user, Model.Password);
if (result.Succeeded)

    await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Email, Model.Email));

向特定用户发送消息。

public class ChatHub : Hub

    public async Task SendMessage(string receiver, string message)
    
        await Clients.User(receiver).SendAsync("ReceiveMessage", message);
    

注意:消息发送者不会收到消息发送通知。如果您想在发件人端收到通知。把SendMessage方法改成这个。

public async Task SendMessage(string sender, string receiver, string message)

    await Clients.Users(sender, receiver).SendAsync("ReceiveMessage", message);

仅当您需要更改默认标识符时才需要执行这些步骤。否则,跳到最后一步,您可以通过将 userIds 或 connectionIds 传递给SendMessage 来简单地发送消息。 For more

【讨论】:

以上是关于SignalR - 使用 (IUserIdProvider) *NEW 2.0.0* 向特定用户发送消息的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core的实时库: SignalR简介及使用

[Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

使用 .NET 客户端时的 SignalR 传输

MVC5使用SignalR进行双向通信

SignalR系列教程:在MVC在使用SignalR

.net core 3.0 Signalr - 05 使用jwt将用户跟signalr关联