ASP.NET Core 使用 SignalR 中的中心Use hubs in SignalR for ASP.NET Core

本文内容

作者: Rachel Appel古柯 Griffin

查看或下载示例代码(如何下载)

什么是 SignalR 中心What is a SignalR hub

通过 SignalR 中心 API,你可以从服务器对连接的客户端调用方法。在服务器代码中,您将定义由客户端调用的方法。在客户端代码中,您将定义从服务器调用的方法。SignalR 负责使实时的客户端到服务器和服务器到客户端的通信成为可能。

配置 SignalR 中心Configure SignalR hubs

SignalR 中间件需要一些服务,这些服务通过调用 services.AddSignalR进行配置。

  1. services.AddSignalR();

将 SignalR 功能添加到 ASP.NET Core 应用时,请通过在 Startup.Configure 方法的 app.UseEndpoints 回调中调用 endpoint.MapHub 来设置 SignalR 路由。

  1. app.UseRouting();
  2. app.UseEndpoints(endpoints =>
  3. {
  4. endpoints.MapHub<ChatHub>("/chathub");
  5. });

将 SignalR 功能添加到 ASP.NET Core 应用时,请通过在 Startup.Configure 方法中调用 app.UseSignalR 来设置 SignalR 路由。

  1. app.UseSignalR(route =>
  2. {
  3. route.MapHub<ChatHub>("/chathub");
  4. });

创建和使用集线器Create and use hubs

通过声明从 Hub继承的类创建一个中心,并向其添加公共方法。客户端可以调用定义为 public的方法。

  1. public class ChatHub : Hub
  2. {
  3. public Task SendMessage(string user, string message)
  4. {
  5. return Clients.All.SendAsync("ReceiveMessage", user, message);
  6. }
  7. }

您可以指定返回类型和参数(包括复杂类型和数组),就像在任何C#方法中一样。SignalR 处理复杂对象和数组在参数和返回值中的序列化和反序列化。

备注

中心是暂时性的:

  • 不要将状态存储在 hub 类的属性中。每个 hub 方法调用都在新的 hub 实例上执行。
  • 调用依赖于中心保持活动状态的异步方法时,请使用 await。例如,如果在没有 await 的情况下调用,则方法(如 Clients.All.SendAsync(…))可能会失败,并且在 SendAsync 完成之前中心方法完成。

上下文对象The Context object

Hub 类具有一个 Context 属性,其中包含有关连接的信息的以下属性:

properties说明
ConnectionId获取由 SignalR 分配的连接的唯一 ID。每个连接都有一个连接 ID。
UserIdentifier获取用户标识符默认情况下,SignalR 使用与连接关联的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作为用户标识符。
User获取与当前用户关联的 ClaimsPrincipal
Items获取可用于在此连接的范围内共享数据的键/值集合。数据可以存储在此集合中,它将在不同的集线器方法调用中持久保存。
Features获取连接上的可用功能的集合。目前,在大多数情况下不需要此集合,因此不会对其进行详细介绍。
ConnectionAborted获取在连接中止时通知的 CancellationToken

Hub.Context 还包含以下方法:

方法说明
GetHttpContext返回连接的 HttpContext,如果连接不与 HTTP 请求关联,则为 null对于 HTTP 连接,可以使用此方法来获取 HTTP 标头和查询字符串等信息。
Abort中止连接。

Clients 对象The Clients object

Hub 类具有一个 Clients 属性,其中包含服务器和客户端之间的通信的以下属性:

properties说明
All在所有连接的客户端上调用方法
Caller在调用集线器方法的客户端上调用方法
Others在所有连接的客户端上调用方法,但调用方法的客户端除外

Hub.Clients 还包含以下方法:

方法说明
AllExcept在所有连接的客户端(指定的连接除外)上调用方法
Client在特定连接的客户端上调用方法
Clients在特定连接的客户端上调用方法
Group对指定组中的所有连接调用方法
GroupExcept对指定组中的所有连接调用方法,指定的连接除外
Groups在多组连接上调用方法
OthersInGroup对一组连接调用方法,而不包括调用该集线器方法的客户端
User对与特定用户关联的所有连接调用方法
Users对与指定用户相关联的所有连接调用方法

上表中的每个属性或方法返回一个具有 SendAsync 方法的对象。利用 SendAsync 方法,你可以提供要调用的客户端方法的名称和参数。

向客户端发送消息Send messages to clients

若要调用特定客户端,请使用 Clients 对象的属性。在下面的示例中,有三种集线器方法:

  • SendMessage 使用 Clients.All将消息发送到所有连接的客户端。
  • SendMessageToCaller 使用 Clients.Caller将消息发送回调用方。
  • SendMessageToGroupsSignalR Users 组中的所有客户端发送一条消息。
  1. public Task SendMessage(string user, string message)
  2. {
  3. return Clients.All.SendAsync("ReceiveMessage", user, message);
  4. }
  5. public Task SendMessageToCaller(string message)
  6. {
  7. return Clients.Caller.SendAsync("ReceiveMessage", message);
  8. }
  9. public Task SendMessageToGroup(string message)
  10. {
  11. return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", message);
  12. }

强类型中心Strongly typed hubs

使用 SendAsync 的缺点是它依赖于幻字符串来指定要调用的客户端方法。如果客户端中的方法名称拼写错误或缺失,则这会使代码对运行时错误开放。

使用 SendAsync 的一种替代方法是使用 Hub<T>强键入 Hub在下面的示例中,已将 ChatHub 客户端方法提取到名为 IChatClient的接口。

  1. public interface IChatClient
  2. {
  3. Task ReceiveMessage(string user, string message);
  4. Task ReceiveMessage(string message);
  5. }

此接口可用于重构前面的 ChatHub 示例。

  1. public class StronglyTypedChatHub : Hub<IChatClient>
  2. {
  3. public async Task SendMessage(string user, string message)
  4. {
  5. await Clients.All.ReceiveMessage(user, message);
  6. }
  7. public Task SendMessageToCaller(string message)
  8. {
  9. return Clients.Caller.ReceiveMessage(message);
  10. }
  11. }

使用 Hub<IChatClient> 启用对客户端方法的编译时检查。这可以防止由于使用神奇字符串而导致的问题,因为 Hub<T> 只能提供对在接口中定义的方法的访问。

使用强类型 Hub<T> 禁用使用 SendAsync的能力。接口上定义的任何方法仍可以定义为异步方法。事实上,其中每个方法都应该返回 Task由于它是一个接口,因此请勿使用 async 关键字。例如:

  1. public interface IClient
  2. {
  3. Task ClientMethod();
  4. }

备注

不会从方法名称中去除 Async 后缀。除非使用 .on('MyMethodAsync')定义了客户端方法,否则不应使用 MyMethodAsync 作为名称。

更改集线器方法的名称Change the name of a hub method

默认情况下,服务器集线器方法名称是 .NET 方法的名称。但是,可以使用HubMethodName属性来更改此默认设置,并手动指定方法的名称。调用方法时,客户端应使用此名称,而不是 .NET 方法名称。

  1. [HubMethodName("SendMessageToUser")]
  2. public Task DirectMessage(string user, string message)
  3. {
  4. return Clients.User(user).SendAsync("ReceiveMessage", message);
  5. }

处理连接事件Handle events for a connection

SignalR 中心 API 提供 OnConnectedAsyncOnDisconnectedAsync 虚拟方法来管理和跟踪连接。重写 OnConnectedAsync 虚方法,以便在客户端连接到集线器时执行操作,如将其添加到组中。

  1. public override async Task OnConnectedAsync()
  2. {
  3. await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
  4. await base.OnConnectedAsync();
  5. }

重写 OnDisconnectedAsync 虚方法,以便在客户端断开连接时执行操作。如果客户端故意断开连接(例如,通过调用 connection.stop()),exception 参数将 null但是,如果客户端由于错误(例如网络故障)而断开连接,则 exception 参数将包含描述失败的异常。

  1. public override async Task OnDisconnectedAsync(Exception exception)
  2. {
  3. await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
  4. await base.OnDisconnectedAsync(exception);
  5. }

警告

安全警告:如果 SignalR 服务器或客户端版本 ASP.NET Core 2.2 或更低版本,则公开 ConnectionId 可能会导致恶意模拟。

处理错误Handle errors

在中心方法中引发的异常将发送到调用方法的客户端。在 JavaScript 客户端上,invoke 方法返回Javascript 承诺当客户端使用 catch收到包含附加到承诺的处理程序的错误时,将调用该处理程序并将其作为 JavaScript Error 对象进行传递。

  1. connection.invoke("SendMessage", user, message).catch(err => console.error(err));

如果中心引发异常,则不会关闭连接。默认情况下,SignalR 会向客户端返回一般的错误消息。例如:

  1. Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.

意外的异常通常包含敏感信息,例如数据库连接失败时触发的异常中的数据库服务器的名称。默认情况下,SignalR 不会公开这些详细的错误消息作为一种安全措施。有关抑制异常详细信息的原因的详细信息,请参阅安全注意事项一文

如果要将异常情况传播到客户端,则可以使用 HubException 类。如果从中心方法引发 HubException,SignalR会将整个消息发送到客户端(未修改)。

  1. public Task ThrowException()
  2. {
  3. throw new HubException("This error will be sent to the client!");
  4. }

备注

SignalR 仅将异常的 Message 属性发送到客户端。异常的堆栈跟踪和其他属性不适用于客户端。

相关资源Related resources