如何使用aspnetcore signalR向特定用户发送消息?

7

我需要向特定用户发送通知。我正在使用dotnet core 3.1和ASPNETCORE SignalR进行通知。我可以将消息发送到所有客户端,但无法针对特定用户执行此操作。

编辑1

我的Hub代码如下:

public class NotificationHub : Hub
{
    public override async Task OnConnectedAsync()
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception ex)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
        await base.OnDisconnectedAsync(ex);
    }
}

我正在从控制器调用SendAsync方法:

 private IHubContext<NotificationHub> _hub;

    public NotificationController(IHubContext<NotificationHub> hub)
    {
        _hub = hub;
    }

[HttpGet]
    public IActionResult Get()
    {
        //_hub.Clients.All.SendAsync("SendMessage",
        _hub.Clients.All.SendAsync("SendMessage",
            new
            {
                val1 = getRandomString(),
                val2 = getRandomString(),
                val3 = getRandomString(),
                val4 = getRandomString()
            });

        return Ok(new { Message = "Request Completed" });
    }
4个回答

3

您可以通过用户 ID向特定用户发送消息。

在此示例中,我们将尝试获取当前用户信息并使用用户 ID 向该用户发送一些数据:

在 Hub 中:

public async Task GetInfo()
{
    var user = await _userManager.GetUserAsync(Context.User);

    await Clients.User(user.Id).SendCoreAsync("msg", new object[] { user.Id, user.Email });
}

在客户端:

connection.on('msg', function (...data) {
    console.log('data:', data); // ["27010e2f-a47f-4c4e-93af-b55fd95f48a5", "foo@bar.com"]
});

connection.start().then(function () {
    connection.invoke('getinfo');
});

注意: 确保你已经在UseEndpoints方法内映射了该中心:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapHub<YourHubName>("/yourhubname");
});

1
要向特定用户发送消息,只需调用以下代码:
_hubContext.Clients.User(useridentifier).SendAsync("my_message");
//_hubContext is your context, can be a DI injected variable etc

如果您正在使用ASP.NET Core内置身份验证,则只需在您的Hub中拥有[Authorize]即可正常工作。
但是,如果您正在使用自定义身份验证,例如CookieAuthentication,则必须向SignalR提供一个“name-provider”,以便它知道将哪个用户放入哪个桶中。
像这样:
public class NameUserIdProvider : IUserIdProvider
{
    public string GetUserId(HubConnectionContext connection)
    {
        //for example just return the user's username
        return connection.User?.Identity?.Name;
    }
}

然后在 Startup.cs 中注册这个 "provider"。

//needed for SignalR to support "Context.UserIdentifier" and built-in user mapping
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();

1
你可以发送通知到指定的用户,如果你知道用户的connectionId,或者将已连接的用户添加到一个组中。
在hub中,假设你知道connectionId:
await this.Clients.Client("connectionId").SendAsync("MethodName", "The message");

您还可以将指定用户添加到一个群组中,然后向该群组发送消息:
await this.Groups.AddToGroupAsync("connectionId", "groupName");

await this.Clients.Group("groupName").SendAsync("MethodName", "The message");

你可以在这个Microsoft文档中了解更多相关内容。 更新: 回答你的更新问题,你必须向你的信道提供授权属性才能拥有身份名称和其他参数。
[Authorize]
public class NotificationHub : Hub
{
    public override async Task OnConnectedAsync()
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception ex)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
        await base.OnDisconnectedAsync(ex);
    }
}

然后在您的Angular客户端上,您必须提供一个令牌来连接到您的Hub,例如:

private configureSignalR(token: string) {
    this.hubMessageConnection = new signalR.HubConnectionBuilder()
    .configureLogging(signalR.LogLevel.Error).withUrl(this.signalRUrl + "/notifications",
    {
        accessTokenFactory: () => token
    })
    .withAutomaticReconnect()
    .build();
}

您可以在微软文档中了解有关身份验证和授权的更多信息。


我已更新我的问题并展示了如何使用Hub及其功能。await Groups.AddToGroupAsync(Context.ConnectionId,Context.User.Identity.Name);但是我得到的Context.User.Identity.Name为null。我做错了什么。我的客户端是Angular应用程序。 - Akshay Kumar Sharma

0

我认为在ASP.NET Core SignalR中存在一个bug(我也在使用ASP.NET Core 3.1)。

_hubContext.Clients.User(userId).SendAsync(...)

似乎不起作用。请注意,默认情况下,userId 是存储在基于声明的身份验证中的 ClaimTypes.NameIdentifier来自文档:默认情况下,SignalR 使用与连接关联的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作为用户标识符。

因此,我不得不创建自己的连接管理器,它可以正常工作,一旦获取到所有用户的连接,您可以循环并编写类似以下内容的代码:

_hubContext.Clients.Client(connectionId).SendAsync(...)

这里有一个关于如何管理用户连接的提示:

public class YourHub : Hub
        ...
        public override Task OnConnectedAsync()
        {    
            //Get userId
            _connectionHubManager.AddConnection(userId, Context.ConnectionId);
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception exception)
        {
            _connectionHubManager.RemoveConnection(Context.ConnectionId);
            return base.OnDisconnectedAsync(exception);
        }

如何获取userId取决于您的实现方式。

如果userId已注册为ClaimTypes.Name,您可以简单地获取它:

Context.User.Identity.Name;

如果userId已注册为ClaimTypes.NameIdentifier,则需要执行以下操作:
(Context.User.Identity as ClaimsIdentity)?.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value;

不要忘记在您的信号中心类中添加[Authorize]以便填充这些字段并查看文档以获取SignalR身份验证。

我已经找到了这个问题的答案,很快会更新我的回答版本。 - Akshay Kumar Sharma
@AkshayKumarSharma 你好,你能添加你的答案吗? - Divyang Desai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接