Solución:
SignalR proporciona ConnectionId para cada conexión. Para encontrar qué conexión pertenece a quién (el usuario), necesitamos crear un mapeo entre la conexión y el usuario. Esto depende de cómo identifique a un usuario en su aplicación.
En SignalR 2.0, esto se hace mediante el uso de IPrincipal.Identity.Name
, que es el identificador de usuario conectado según se estableció durante la autenticación ASP.NET.
Sin embargo, es posible que deba asignar la conexión con el usuario utilizando un identificador diferente en lugar de utilizar Identity.Name. Para este propósito, este nuevo proveedor se puede utilizar con su implementación personalizada para mapear al usuario con la conexión.
Ejemplo de asignación de usuarios de SignalR a conexiones mediante IUserIdProvider
Supongamos que nuestra aplicación utiliza un userId
para identificar a cada usuario. Ahora, necesitamos enviar un mensaje a un usuario específico. Tenemos userId
y message
, pero SignalR también debe conocer el mapeo entre nuestro userId y la conexión.
Para lograr esto, primero necesitamos crear una nueva clase que implemente 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();
}
}
El segundo paso es decirle a SignalR que use nuestro CustomUserIdProvider
en lugar de la implementación predeterminada. Esto se puede hacer en Startup.cs mientras se inicializa la configuración del concentrador:
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();
}
}
Ahora, puede enviar un mensaje a un usuario específico usando su userId
como se menciona en la documentación, como:
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
Espero que esto ayude.
Aquí es un comienzo … Abierto a sugerencias / mejoras.
Servidor
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("[email protected]").addChatMessage(name, message);
}
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
Groups.Add(Context.ConnectionId, name);
return base.OnConnected();
}
}
JavaScript
(Date cuenta cómo addChatMessage
y sendChatMessage
también son métodos en el código del servidor anterior)
$(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();
});
});
});
Pruebas
Así es como se usa SignarR para dirigirse a un usuario específico (sin usar ningún proveedor):
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!");