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

时间:2013-10-22 15:27:59

标签: asp.net asp.net-mvc real-time signalr

在最新版本的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的声明草案带有这个bug并且没有编译。有没有人实现过这个功能?

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

拥抱。

6 个答案:

答案 0 :(得分:135)

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

希望这有帮助。

答案 1 :(得分:34)

这是一个开始......接受建议/改进。

服务器

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

测试 enter image description here

答案 2 :(得分: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!");

答案 3 :(得分:2)

查看SignalR Tests功能。

测试“SendToUser”自动使用常规owin身份验证库传递用户身份。

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

答案 4 :(得分:0)

旧线程,但在示例中遇到了这个问题:

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

答案 5 :(得分:0)

对于任何试图在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);
}

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