Reputation: 55467
I'm working through the SignalR
Microsoft tutorial located here.
Everything is working ok, however, I wanted to try to change the code in ChatHub#SendMessage
to send a message to a specific user.
So I modified the code like this:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
//await Clients.All.SendAsync("ReceiveMessage", user, message);
await Clients.User(user).SendAsync("ReceiveMessage", message);
}
}
However, the user never gets the message. The message is only sent if you use Clients.All.SendAsync
(that's the line that's commented out above).
I also did more research and found from this answer that maybe I need to implement a custom user provider. So I implemented one:
public class CustomUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return "user1";
}
}
Then in Startup.cs
, I registered this provider:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
services.AddSignalR();
}
Then I tried using the Context.UserIdentifier
when sending the message, like this:
public async Task SendMessage(string user, string message)
{
//await Clients.All.SendAsync("ReceiveMessage", user, message);
await Clients.User(Context.UserIdentifier).SendAsync("ReceiveMessage", message);
}
But it still does not seem to send the message.
What am I doing wrong?
Upvotes: 1
Views: 4743
Reputation: 13607
The way I do it is when a user logs in I add them to a SignalR Group, the Group being the user's ID or Name. that way it's a unique group for just that one user. Then I send the message to that Group. Only that user will get that message because they are the only one in that group.
Client Side Code:
// Optional: authenticate with a server, get a token... or send username/password in following request
_hub = new HubConnectionBuilder()
.WithUrl(_envSettings.CreatePlayerServiceHubPath(), x =>
{
x.Headers.Add("x-lcc-version", AppEnvironment.Application.Version);
x.Headers.Add("x-lcc-username", username);
x.Headers.Add("x-lcc-authorization", token);
})
.AddNewtonsoftJsonProtocol()
.Build();
_hub.Closed += OnSignalRDisconnectedFromServerAsync;
SetupRPCs();
await _hub.StartAsync().ConfigureAwait(false);
Server Side Code:
public override async Task OnConnectedAsync()
{
var userName = Context.GetHttpContext().Request.Headers["x-lcc-username"];
var token = Context.GetHttpContext().Request.Headers["x-lcc-authorization"];
// Optional: validate authorization
await Groups.AddToGroupAsync(Context.ConnectionId, userName);
// Then when you want to send a message to the user:
await Clients.Group(userName).SendAsync("hello", "world");
}
What's nice is that when the user disconnects, the Group
automatically disappears. You don't have to maintain those groups you are creating.
I know this may not be 100% what you are looking for, but it's an idea of how to handle similar situations with other scenarios.
Upvotes: 3
Reputation: 55467
I found the issue. I'd left off the user parameter in the Clients.User(user).SendAsync
call.
So it should be like this:
public async Task SendMessage(string user, string message)
{
//await Clients.All.SendAsync("ReceiveMessage", user, message);
await Clients.User(user).SendAsync("ReceiveMessage", user, message); // <-- add user parameter
}
Upvotes: 3