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>: ' + 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"])
};
});