ASP.NET Core SignalR 的運作方式

已完成

伺服器和 Hub 類別

Hub 類別是 SignalR 伺服器概念。 其定義在 Microsoft.AspNetCore.SignalR 命名空間內,而且是 Microsoft.AspNetCore.SignalR NuGet 套件的一部分。 以 Microsoft.NET.Sdk.Web SDK 為目標的 ASP.NET Core Web 應用程式不需要新增 SignalR 的套件參考,因為它已經作為共用架構的一部分來使用。

Hub 會透過路由公開。 例如,https://www.contoso-pizza.com/hubs/orders 路由可用於表示 OrdersHub 實作。 透過各種中樞 API,作者可以定義方法和事件。

在中樞上公開方法的形式有兩種。 您可建立下列類型的子類別及撰寫方法的

  • Hub:標準中樞。
  • Hub<T>:強型別泛型中樞。

範例 Hub

做為參考點,請考慮下列 Notification 物件:

namespace RealTime.Models;

public record Notification(string Text, DateTime Date);

此物件可以在您使用 .NET 用戶端 SDK 時共用,讓伺服器和用戶端擁有完全相同的物件。 想像通知中樞:

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleServer.Hubs;

public sealed class NotificationHub : Hub
{
    public Task NotifyAll(Notification notification) =>
        Clients.All.SendAsync("NotificationReceived", notification);
}

關於方法與事件之間的差異,上述中樞實作中的方法為 NotifyAll,而事件為 NotificationReceivedNotificationHubHub 的子類別。 NotifyAll 方法會傳回 Task,並接受單一的 Notification 參數。 方法會以從 Clients.AllSendAsync 的引動過程表示,這代表所有已連線的用戶端。 NotificationReceived 事件會依據 notification 執行個體引發。

IHubContext 執行個體

您可以從 HubIHubContext 執行個體引發事件。 SignalR 中樞是將訊息傳送至已連線至 SignalR 伺服器的用戶端核心抽象概念。 您也可使用下列其中一種類型,從應用程式中的其他位置傳送訊息:

重要

IHubContext 用於將通知傳送給用戶端。 其不會用於在 Hub 上呼叫方法。

範例 IHubContext

考慮先前的通知中樞實作,您可以使用 IHubContext<NotificationHub>,如下所示:

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleServer.Services;

public sealed class NotificationService(
    IHubContext<NotificationHub> hubContext)
{
    public Task SendNotificationAsync(Notification notification) =>
        notification is not null
            ? hubContext.Clients.All.SendAsync("NotificationReceived", notification)
            : Task.CompletedTask;
}

上述 C# 程式碼仰賴 IHubContext<NotificationHub> 存取用戶端的內容清單,公開廣播通知的能力。 在範圍中擷取的 hubContext 主要建構函式參數會用於引發 "NotificationReceived" 事件,但其目的並非用於呼叫中樞的 NotifyAll 方法。

方法

HubHub<T> 方法就像任何其他 C# 方法一樣。 其會定義傳回型別、方法名稱和參數。

  • 中樞方法最常見的傳回型別是 TaskTask<TResult>,其代表非同步中樞作業。
  • 方法名稱用於從用戶端呼叫方法。 您可以使用 HubMethodNameAttribute 來加以自訂。
  • 參數是選擇性的,但是定義參數時,用戶端應該會提供對應的引數。

引發事件不需要方法,但通常需要。

事件

事件可以依名稱從用戶端訂閱。 伺服器負責引發事件。 HubHub<T>IHubContext<THub>IHubContext<THub, T> 事件會命名,而且最多可以定義 10 個參數。 事件會在伺服器上引發,並由感興趣的用戶端處理。 當用戶端訂閱其中樞連線上的事件時,用戶端會被視為有興趣。 當用戶端呼叫因為其而引動過程以致引發事件的中樞方法時,用戶端可以間接觸發事件。 不過,用戶端無法直接觸發事件,因為這是伺服器的責任。

事件用戶端範圍

您可以從 IClientProxy 執行個體呼叫事件。 您可以從 Clients 類型實作 IHubClientsIHubCallerClients 介面。 有多種方式可將範圍限定於特定 IClientProxy 執行個體。 您可以從 Hub.Clients 屬性以下列範圍為目標:

member 詳細資料
All 所有已連線的用戶端 (例如廣播)。
AllExcept 所有已連線的用戶端,不包括指定的連線 (例如已篩選的廣播)。
Caller 觸發方法的已連線用戶端 (例如回應)。
Client 指定的用戶端連線 (單一連線)。
Clients 指定的用戶端連線 (多重連線)。
Group 指定群組內的所有已連線用戶端。
GroupExcept 指定群組內的所有已連線用戶端,不包括指定的連線。
Groups 指定群組內的所有已連線用戶端 (多重群組)。
Others 所有已連線的用戶端,不包括觸發方法的用戶端。
OthersInGroup 指定的群組內的所有已連線用戶端,不包括觸發方法的用戶端。
User 指定使用者的所有已連線用戶端 (單一使用者可以在多個裝置上連線)。
Users 指定使用者的所有已連線用戶端。

範例範圍

請考慮下列影像,其可協助您將中樞傳送訊息至目標用戶端的方式視覺化。 您可以展開影像,以改善可讀性。

  • 向所有人廣播

    ASP.NET Core SignalR hub sending message with Clients.All syntax.

    不論已連線的用戶端是否可能屬於的群組,所有已連線的用戶端都會接收此訊息。

  • 隔離的使用者

    ASP.NET Core SignalR hub sending message with Clients.User syntax.

    無論目前使用多少裝置,單一使用者都會收到此訊息。

  • 隔離的群組

    ASP.NET Core SignalR hub sending message with Clients.Group syntax.

    只有屬於特定群組的用戶端會收到此訊息。

用戶端和 HubConnection 類別

HubConnection 類別是 SignalR 用戶端概念,代表用戶端與伺服器 Hub 的連線。 其定義在 Microsoft.AspNetCore.SignalR.Client 命名空間內,而且是 Microsoft.AspNetCore.SignalR.Client NuGet 套件的一部分。

您可使用建立器模式和對應的 HubConnectionBuilder 類型來建立一個 HubConnection。 假設您已知中樞的路由 (或 System.Uri),您可以建立一個 HubConnection。 建立器也可以指定其他組態選項 (包括記錄、所需的通訊協定、驗證權杖轉送,以及自動重新連線等等)。

HubConnection API 會公開開始和停止函式,您可用來開始及停止與伺服器的連線。 此外,還有串流、呼叫中樞方法,以及訂閱事件的功能。

範例 HubConnection 建立

若要從 .NET SignalR 用戶端 SDK 建立 HubConnection 物件,請使用 HubConnectionBuilder 類型:

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleClient;

public sealed class Consumer : IAsyncDisposable
{
    private readonly string HostDomain =
        Environment.GetEnvironmentVariable("HOST_DOMAIN");
    
    private HubConnection _hubConnection;

    public Consumer()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(new Uri($"{HostDomain}/hub/notifications"))
            .WithAutomaticReconnect()
            .Build();
    }

    public Task StartNotificationConnectionAsync() =>
        _hubConnection.StartAsync();

    public async ValueTask DisposeAsync()
    {
        if (_hubConnection is not null)
        {
            await _hubConnection.DisposeAsync();
            _hubConnection = null;
        }
    }
}

呼叫中樞方法

如果為用戶端提供了一個已成功啟動的用戶端 HubConnection 執行個體,則該用戶端可以使用 InvokeAsyncSendAsync 擴充功能來呼叫中樞上的方法。 如果中樞方法傳回 Task<TResult>,則 InvokeAsync<TResult> 的結果為 TResult 類型。 如果中樞方法傳回 Task,則沒有任何結果。 InvokeAsyncSendAsync 都需要中樞方法的名稱,以及零到 10 個參數。

  • InvokeAsync:使用指定的方法名稱和選擇性引數叫用伺服器上的中樞方法。
  • SendAsync:使用指定的方法名稱和選擇性引數叫用伺服器上的中樞方法。 此方法不會等候接收者的回應。

範例中樞方法引動過程

SendNotificationAsync 將方法新增至前一個 Consumer 類別時,SendNotificationAsync 會委派給 _hubConnection 並呼叫伺服器的中樞上的 NotifyAll 方法 (取決於 Notification 執行個體)。

public Task SendNotificationAsync(string text) =>
    _hubConnection.InvokeAsync(
        "NotifyAll", new Notification(text, DateTime.UtcNow));

處理事件

若要處理事件,您可使用 HubConnection 執行個體註冊處理常式。 當您知道中樞方法的名稱且具有零到八個參數時,請呼叫其中一個 HubConnectionExtensions.On 多載。 處理常式可以滿足下列任何 Action 變化:

或者,您可以使用非同步處理常式 API,這是 Func<TResult>,其中 TResultTask 變化:

註冊事件處理常式的結果為一個 IDisposable,其做為訂用帳戶。 若要取消訂閱處理常式,請呼叫 Dispose

範例事件註冊

更新先前的 Consumer 類別,您可以提供處理常式並呼叫 On 來註冊事件:

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleClient;

public sealed class Consumer : IAsyncDisposable
{
    private readonly string HostDomain =
        Environment.GetEnvironmentVariable("HOST_DOMAIN");
    
    private HubConnection _hubConnection;

    public Consumer()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(new Uri($"{HostDomain}/hub/notifications"))
            .WithAutomaticReconnect()
            .Build();

        _hubConnection.On<Notification>(
            "NotificationReceived", OnNotificationReceivedAsync);
    }

    private async Task OnNotificationReceivedAsync(Notification notification)
    {
        // Do something meaningful with the notification.
        await Task.CompletedTask;
    }

    // Omitted for brevity.
}

當伺服器的中樞執行個體引發 "NotificationReceived" 事件時,就會呼叫 OnNotificationReceivedAsync 方法。

Contoso Pizza 即時訂單更新

Web 應用程式的伺服器程式碼必須具有 Hub 實作,並且對用戶端公開路由。 Hub 可以使用訂單物件的唯一識別碼來建立群組以供追蹤。 然後,所有訂單狀態變更更新都可以在此群組中進行傳達。

用戶端程式碼也需要更新,以指出 Contoso Pizza 應用程式是 Blazor WebAssembly 應用程式。 您可以使用 JavaScript SDK 或 .NET 用戶端 SDK。 您會將用戶端輪詢功能取代為可建置 HubConnection 的程式碼,然後開始進行與伺服器的連線。 當其瀏覽至訂單追蹤頁面時,程式碼必須加入訂單的特定群組,變更更新會傳送到其中。 您可以訂閱訂單狀態變更的事件,然後對其進行相應的處理。