此为系列文章,对MSDN ASP.NET Core SignalR 的官方文档进行系统学习与翻译。其中或许会添加本人对 ASP.NET Core 的浅显理解。
给客户端发送消息
为了调用特定的客户端,我们可以使用 Clients 对象的属性。在如下的示例中,有三个 中心 方法:
- SendMessage,向所有已连接的客户端发送消息,使用
Clients.All。
SendMessageToCaller,向调用者回发一个消息,使用
Clients.Caller。
SendMessageToGroups,向“SignalR Users”分组的所有客户端发送消息。
public Task SendMessage(string user, string message) { return Clients.All.SendAsync("ReceiveMessage", user, message); } public Task SendMessageToCaller(string message) { return Clients.Caller.SendAsync("ReceiveMessage", message); } public Task SendMessageToGroup(string message) { return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", message); }
强类型的 中心(hubs)
使用SendAsync
的一个缺点是它必须要依赖一个魔法字符串来指定所要调用的客户端方法。这样的话,如果方法名称拼写错误或者根本不存在于客户端,那么便会发生运行时错误。
使用SendAsync的一个替代方法便是使用Hub<T> 将 中心 强类型化。在如下的示例中,ChatHub
客户端方法被踢出去到一个名为 IChatClient 的接口中:
public interface IChatClient { Task ReceiveMessage(string user, string message); Task ReceiveMessage(string message); }
这个接口可用来重构之前 ChatHub
的例子。
public class StronglyTypedChatHub : Hub<IChatClient> { public async Task SendMessage(string user, string message) { await Clients.All.ReceiveMessage(user, message); } public Task SendMessageToCaller(string message) { return Clients.Caller.ReceiveMessage(message); } }
使用Hub<IChatClient> 可以启用客户端方法的编译时检查,这可以阻止使用魔法字符串导致的问题,因为 Hub<T> 仅仅提供了对定义在接口中的方法的访问。
使用强类型的 Hub<T> 同时也禁用了 使用 SendAsync 的能力。定义在接口中的方法仍然可以被定义为异步的。事实上,这些方法都应该返回一个 Task。因为它是一个接口,就不要使用 async 关键字,举个例子:
public interface IClient { Task ClientMethod(); }
注意:Async
后缀不会从方法名中移除。除非你的客户端方法被定义为 .on('MyMethodAsync'),否则你不该使用 MyMethodAsync 作为名称。
改变一个 中心 方法的名称
默认的,一个服务端 中心 方法的名称是一个.NET 方法的名称。不过你也可以使用 HubMethodName 属性来更改这个默认设置并为方法指定一个名称。当调用方法时,客户端应该使用这个名称,而不是.NET 方法名。
[HubMethodName("SendMessageToUser")] public Task DirectMessage(string user, string message) { return Clients.User(user).SendAsync("ReceiveMessage", message); }
为一个连接处理事件
SignalR Hubs API 提供了 OnConnectedAsync 方法 和 OnDisconnectedAsync 虚方法来管理和追踪连接。当一个客户端连接到 中心 时,我们可以重载 OnConnectedAsync 虚方法来执行一些动作,比如将它添加到一个分组。
public override async Task OnConnectedAsync() { await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnConnectedAsync(); }
当一个客户端断开连接时,重载OnDisconnectedAsync
虚方法以执行动作。如果客户端是有意断开(比如调用 connection.stop()),那么 exception 参数便会是 null。然而,如果客户端是有由于错误而断开(比如网络错误),exception 参数便会包含描述了失败信息的异常。
public override async Task OnDisconnectedAsync(Exception exception) { await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnDisconnectedAsync(exception); }
警告:安全警告,如果SignalR 服务端 和 客户端是ASP.NET Core 2.2 及较早版本,暴漏ConnectionId
会导致恶意模仿。
处理错误
在你的 中心 方法中抛出的异常将会发送给调用它的客户端。在 Javascript 端,Invoke 方法返回一个 JavaScript Promise。当客户端使用catch接收到带有附加到promise的处理程序的错误时,它将被调用并作为JavaScript错误对象传递。
connection.invoke("SendMessage", user, message).catch(err => console.error(err));
如果你的 中心 抛出一个异常,那么连接不会被关闭。默认的,SignalR 返回一个通用错误消息给客户端,比如:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.
不预期的异常往往包含一些敏感信息,比如当数据库连接失败时,在触发的异常中的数据库服务器的名称。出于安全原因,默认情况下SignalR 不会暴漏这些详细的错误消息。关于因此隐藏异常详情的原因的更多信息,请参考 Security considerations article。
如果你有确实需要传播给客户端的异常情况,你可以使用HubException 类,如果你从中心方法中抛出了一个 HubException,SignalR 会将整个异常信息不做修改的发送给客户端。
public Task ThrowException() { throw new HubException("This error will be sent to the client!"); }
注意,SignalR仅仅将异常的Message 属性发送给客户端。而异常的 栈跟踪及其他属性 对于客户端来说是不可用的。
相关资源