[譯] 在 ASP.NET Core 中使用 SignalR

weixin_34194317發表於2017-10-28

weblogs.asp.net/ricardopere…
作者:Ricardo Peres
譯者:oopsguy.com

介紹

SignalR 是一個用於實現實時網站的 Microsoft .NET 庫。它使用多種技術來實現伺服器與客戶端間的雙向通訊,伺服器可以隨時將訊息推送到連線的客戶端。

它現在可在 ASP.NET Core 預發行版本中使用(譯者:根據原文的釋出時間)。我已經介紹過幾次 SignalR 了。

安裝

您將需要安裝 Microsoft.AspNetCore.SignalR.ClientMicrosoft.AspNetCore.SignalR Nuget 預發行包。此外,您將需要 NPM(Node 包管理器)。安裝 NPM 後,您需要獲取 @aspnet/signalr-client 包,之後您需要從 node_modules\@aspnet\signalr-client\dist\browser 資料夾中 獲取 signalr-client-1.0.0-alpha1-final.js 檔案(版本可能不同) ,並將其放置在 wwwroot 資料夾下,以便您可以從頁面引用到它。

接下來,我們需要在使用之前在 ConfigureServices 中註冊所需的服務:

services.AddSignalR();複製程式碼

我們將實現一個簡單的聊天客戶端,因此將在 Configure 方法中註冊一個 ChatHub:

app.UseSignalR(routes =>
{
    routes.MapHub<ChatHub>("chat");
});複製程式碼

注意:UseSignalR 必須在 UseMvc 之前呼叫!

如果你有不同的端點,可以為任意數量的 hub 執行此操作。

在您的檢視或佈局檔案中,新增對 signalr-client-1.0.0-alpha1-final.js 檔案的引用:

<script src="libs/signalr-client/signalr-client-1.0.0-alpha1-final.js"></script>複製程式碼

實現 Hub

該 hub 是一個繼承了 Hub 的類。您可在其中新增 JavaScript 可能呼叫到的方法。我們將實現一個 chat hub::

public class ChatHub : Hub
{
    public async Task Send(string message)
    {
        await this.Clients.All.InvokeAsync("Send", message);
    }
}複製程式碼

如你所見,我們有一個方法(Send),在這個例子中,它採用單個引數(message)。您不需要在廣播呼叫(InvokeAsync)上傳遞相同的引數,可以傳送任何您想要的。

回到客戶端部分,在引用 SignalR JavaScript 檔案後新增此程式碼:

   <script>

        var transportType = signalR.TransportType.WebSockets;
        //can also be ServerSentEvents or LongPolling
        var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
        var chatHub = new signalR.HttpConnection(`http://${document.location.host}/chat`, { transport: transportType, logger: logger });
        var chatConnection = new signalR.HubConnection(chatHub, logger);

        chatConnection.onClosed = e => {
            console.log('connection closed');
       };

       chatConnection.on('Send', (message) => {
           console.log('received message');
       });

       chatConnection.start().catch(err => {
           console.log('connection error');
       });

       function send(message) {
           chatConnection.invoke('Send', message);
       }

</script>複製程式碼

請注意:

  1. 建立指向當前 URL 的連線新增了 chat 字尾,這與在 MapHub 中註冊的一致
  2. 它使用特定的傳輸進行初始化(本例中是 WebSockets),但這不是必需的,也就是說,您可以讓 SignalR 自己採用合適的方式。對於某些作業系統(如 Windows 7),您可能無法使用 WebSockets,因此您必須選擇 LongPollingServerSentEvents
  3. 需要通過呼叫 start 來初始化連線
  4. 有一個 Send 方法的 handler,它與 ChatHubSend 方法有相同的單個引數(message)

所以,每當有人訪問此頁面並呼叫 JavaScript send函式時,它將呼叫 ChatHub 類上的 Send 方法。 該類基本上會向所有連線的客戶端(Clients.All)廣播此訊息。 也可以將訊息傳送到特定的組:

await this.Clients.Group("groupName").InvokeAsync("Send", message);複製程式碼

或特定客戶端:

await this.Clients.Client("id").InvokeAsync("Send", message);複製程式碼

如果使用身份驗證,您可以新增一個由連線 ID 和 ClaimPrincipal 標識的使用者,如下所示:

public override Task OnConnectedAsync()
{
    this.Groups.AddAsync(this.Context.ConnectionId, "groupName");

    return base.OnConnectedAsync();
}複製程式碼

是的,OnConnectedAsync 在新使用者連線時將被呼叫。當有人斷開連線時,OnDisconnectedAsync 將被呼叫:

public override Task OnDisconnectedAsync(Exception exception)
{
    return base.OnDisconnectedAsync(exception);
}複製程式碼

如果在斷開連線時發生一些異常,則 exception 引數將為非空值。
只有當前使用者進行身份驗證時 Context 屬性才會提供 ConnectionIdUser 兩個屬性。ConnectionId 始終被設定為同一個使用者,不會改變。
另一個例子,假設你想通過定時器 hub 將定時器 tick 傳送到所有連線的客戶端。 您可以在 Configure 方法中執行此操作:

TimerCallback callback = (x) => {
    var hub = serviceProvider.GetService<IHubContext<TimerHub>>();
    hub.Clients.All.InvokeAsync("Notify", DateTime.Now);
};

var timer = new Timer(callback);
timer.Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(10));複製程式碼

我們啟動了一個 Timer,從那裡我們得到了一個定時器 hub 的引用,並使用當前時間戳呼叫其 Notify 方法。TimerHub 類只是這樣:

public class TimerHub : Hub
{
}複製程式碼

請注意,此類沒有公共方法,因為它不是由 JavaScript 呼叫,它僅用於從外部廣播訊息(Timer 回撥)。

將訊息傳送到 Hub

最後,還可以將訊息從外部傳送到 hub。當使用控制器時,您需要注入一個 IHubContext 例項,您可以從中傳送訊息到 hub,然後將其適當地廣播:

private readonly IHubContext<ChatHub> _context;

[HttpGet("Send/{message}")]
public IActionResult Send(string message)
{
    //for everyone
    this._context.Clients.All.InvokeAsync("Send", message);
    //for a single group
    this._context.Clients.Group("groupName").InvokeAsync("Send", message);
    //for a single client
    this._context.Clients.Client("id").InvokeAsync("Send", message);

    return this.Ok();
}複製程式碼

請注意,這與訪問 ChatHub 類不同,您無法簡單實現,需要通過 chat hub 的連線才行。

結論

SignalR 尚未釋出,仍可能會發生一些變化。在以後的文章中,我將更詳細地介紹 SignalR,包括其可擴充套件性機制和一些更高階的使用場景。敬請期待!

關注公眾號 oopsguy_com
關注公眾號 oopsguy_com

相關文章