SignalR - отправка сообщения конкретному пользователю с помощью (IUserIdProvider) *NEW 2.0.0*

В последней версии 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);
   }
}

У меня вопрос: откуда мне знать, кому я отправляю свое сообщение? Объяснение этого нового метода очень поверхностно. И проект Statement of SignalR 2.0.0 с этим багом и не компилируется. Кто-нибудь реализовал эту функцию?

Дополнительная информация: http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections

Объятия.

4 ответа

Решение

SignalR предоставляет ConnectionId для каждого соединения. Чтобы выяснить, какое соединение кому принадлежит (пользователю), нам нужно создать отображение между соединением и пользователем. Это зависит от того, как вы идентифицируете пользователя в вашем приложении.

В SignalR 2.0 это делается с помощью встроенного IPrincipal.Identity.Name, который является зарегистрированным идентификатором пользователя, установленным во время аутентификации ASP.NET.

Однако может потребоваться сопоставить соединение с пользователем, используя другой идентификатор, а не Identity.Name. Для этой цели этот новый поставщик может использоваться с вашей пользовательской реализацией для сопоставления пользователя с соединением.

Пример отображения пользователей SignalR на соединения с помощью IUserIdProvider

Предположим, что наше приложение использует userId идентифицировать каждого пользователя. Теперь нам нужно отправить сообщение конкретному пользователю. У нас есть userId а также message, но SignalR также должен знать соответствие между нашим идентификатором пользователя и соединением.

Чтобы достичь этого, сначала нам нужно создать новый класс, который реализует 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);
   }
}

Надеюсь это поможет.

Вот начало.. Открыто для предложений / улучшений.

сервер

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

(Обратите внимание, как addChatMessage а также sendChatMessage также методы в коде сервера выше)

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

тестирование

Вот как использовать 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!");

Для всех, кто пытается сделать это в ядре 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);
}

Эти шаги необходимы только в том случае, если вам нужно изменить идентификатор по умолчанию. В противном случае перейдите к последнему шагу, где вы можете просто отправлять сообщения, передав идентификаторы пользователей или подключения в SendMessage. Для большего

Посмотрите на SignalR Tests для этой функции.

Тест "SendToUser" автоматически принимает идентификатор пользователя, переданный с помощью обычной библиотеки аутентификации owin.

Сценарий заключается в том, что у вас есть пользователь, который подключился с нескольких устройств / браузеров, и вы хотите отправить сообщение всем его активным подключениям.

Старый поток, но только что наткнулся на это в образце:

services.AddSignalR()
            .AddAzureSignalR(options =>
        {
            options.ClaimsProvider = context => new[]
            {
                new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
            };
        });
Другие вопросы по тегам