此為系列文章,對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 屬性發送給客戶端。而異常的 棧跟蹤及其他屬性 對於客戶端來說是不可用的。
相關資源