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

102

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

Объятия.

Игорь
источник
1
Вам нужно изучить аутентификацию и авторизацию с помощью SignalR. UserId будет частью провайдера IPrincipal.
Gjohn

Ответы:

154

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

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

Сумант
источник
Привет, друг, извиняюсь за поздний отзыв! Но я попытался получить доступ к идентификатору, который генерирует CustomUserIdProvider, но метод «OnConnected» не тот. Как мне установить ссылку на пользователя в базе данных? Спасибо!
Игорь
7
Что такое MyCustomUserClass?
Дэнни Буллис,
2
«MyCustomUserClass» может быть вашим пользовательским классом, который содержит метод FindUserId. Это только для примера. У вас может быть любой метод в любом классе, который возвращает вам UserId, и использовать его здесь.
Sumant
5
Спасибо @Sumant, моя проблема заключалась в том, что я был в проекте веб-API, где я реализовал OAuth 2 с токеном-носителем, мне пришлось реализовать логику для передачи токена-носителя в строке запроса, поскольку его нельзя вытащить из заголовки этого начального запроса на подключение сигнализатора. Нельзя просто использовать request.User.Identity.Name
xinunix 02
1
@Sumant Я уже решил это. Проблема заключалась в том, что я вставлял app.MapSignalR();global.asax перед аутентификацией.
Мистер Робот
38

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

Сервер

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

Тестирование введите описание изображения здесь

Человек-маффин
источник
Привет, друг, извиняюсь за поздний отзыв! Но как я могу предотвратить потерю CONNECTIONID? Спасибо.
Игорь
5
@lgao Понятия не имею.
The Muffin Man
зачем нужна эта строка --- Clients.Group ("2@2.com"). addChatMessage (имя, сообщение); ??
Thomas
@Thomas Я, наверное, включил это ради демонстрации. Должен быть другой способ трансляции для конкретной группы, поскольку он был жестко запрограммирован.
The Muffin Man
Это простое решение решило мою проблему с отправкой сообщения конкретному зарегистрированному пользователю. Это просто, быстро и легко понять. Я бы поддержал этот ответ несколько раз, если бы мог.
Rafael AMS
5

Вот как использовать 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!");
Маттео Гариглио
источник
2
как вы это используете, contextIdClientне
Neo
2

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

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

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

Густаво Армента
источник
Спасибо чувак! Но версия проекта 2.0 здесь не компилирует SignalR. : (. К сожалению, я не могу получить к нему доступ.
Игорь
0

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

services.AddSignalR()
            .AddAzureSignalR(options =>
        {
            options.ClaimsProvider = context => new[]
            {
                new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
            };
        });
Грег Гам
источник
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);
}

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

Qudus
источник