Vanik Hachatryan
Vanik Hachatryan

Reputation: 63

How to send a SignalR message to a user?

How does the client authorize to send a message to the user?

Sending from the controller

hubContext.Clients.User(User.Identity.Name).SendAsync();

At the moment the message is not sent. Do I need to add something in OnConnection ()? Or does SignalR have a ready-made mapping mechanism for ConnectionId and User.Identity.Name?

That's how I implemented it at the moment, but it seems to me not quite right. The question is how to make the same standard tools?

    public static class HubConnections
{
    public static Dictionary<string, List<string>> Users = new Dictionary<string, List<string>>();

    public static List<string> GetUserId(string name)
    {
        return Users[name];
    }
}

public class GameHub : Hub
{
    public override Task OnConnectedAsync()
    {


        if (Context.User.Identity.IsAuthenticated 
            && HubConnections.Users.ContainsKey(Context.User.Identity.Name) 
            && !HubConnections.Users[Context.User.Identity.Name].Contains(Context.ConnectionId))
                HubConnections.Users[Context.User.Identity.Name].Add(Context.ConnectionId);
        else 
            HubConnections.Users.Add(Context.User.Identity.Name, new List<string> { Context.ConnectionId });

        return base.OnConnectedAsync();
    }

    public override Task OnDisconnectedAsync(Exception exception)
    {
        if (Context.User.Identity.IsAuthenticated) HubConnections.Users.Remove(Context.User.Identity.Name);

        return base.OnDisconnectedAsync(exception);
    }
}

As I said above, I tried just like this, and it does not work

hubContext.Clients.User(User.Identity.Name).SendAsync();

Upvotes: 6

Views: 6447

Answers (4)

xforfun
xforfun

Reputation: 592

The claim that signalR is using to identify the user can be changed. It is important to ensure that this claim has unique values.

Documentation says to setup a custom UserIdProvider like this:

public class NameUserIdProvider : IUserIdProvider
{
    public string GetUserId(HubConnectionContext connection)
    {
        return connection.User?.Identity?.Name;
    }
}

Add then add it to services:

public void ConfigureServices(IServiceCollection services)
{
    // ... other services ...

    services.AddSignalR();
    services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
}

The snippets are taken from official documentation: https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1#use-claims-to-customize-identity-handling

Upvotes: 1

Derick D&#39;souza
Derick D&#39;souza

Reputation: 86

Was chasing the same issue and got the solution from https://github.com/aspnet/SignalR/issues/2498

One needs to set the NameIdentifier claim. That is the one checked by SignalR instead of the Name claim which I assumed. I set the NameIdentifier claim and I got my non-hub class to send a notification to a specific user.

Upvotes: 3

Simply Ged
Simply Ged

Reputation: 8642

You are using the Users as a store for the connection id. So, for each username, you can send the message to each of the client connections you have stored for that user. Something like this:

public void SendMessage(string username, object data)
{
    var connections = HubConnections.Users[Context.User.Identity.Name];

    foreach(var id in connections)
    {
        Clients.client(id).SendAsync("Foo", data);
    }
}

Upvotes: 0

Chris Pratt
Chris Pratt

Reputation: 239250

There's a client-side component. You must reference the SignalR JS file, create a connection and then subscribe to a particular message from the server. Only then will sending that message actually do something.

<script src="~/lib/signalr/signalr.js"></script>
<script>
    const connection = new signalR.HubConnectionBuilder()
       .withUrl("/gameHub")
       .configureLogging(signalR.LogLevel.Information)
       .build();

    connection.on("Foo", (data) => {
        // do something
    });

    connection.start().catch(err => console.error(err.toString()));
</script>

The above will then cause the client to run the function defined for "Foo" above whenever the server sends a "Foo" message like:

hubContext.Clients.User(User.Identity.Name).SendAsync("Foo", data);

Upvotes: 0

Related Questions