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
做為參考點,請考慮下列 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
,而事件為 NotificationReceived
。 NotificationHub
是 Hub
的子類別。 NotifyAll
方法會傳回 Task
,並接受單一的 Notification
參數。 方法會以從 Clients.All
對 SendAsync
的引動過程表示,這代表所有已連線的用戶端。 NotificationReceived
事件會依據 notification
執行個體引發。
IHubContext
執行個體
您可以從 Hub
或 IHubContext
執行個體引發事件。 SignalR 中樞是將訊息傳送至已連線至 SignalR 伺服器的用戶端核心抽象概念。 您也可使用下列其中一種類型,從應用程式中的其他位置傳送訊息:
- IHubContext<THub>:
THub
代表標準中樞的內容。 - IHubContext<THub,T>:
THub
代表強型別泛型中樞,而T
代表所對應用戶端類型的內容。
重要
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
方法。
方法
Hub
或 Hub<T>
方法就像任何其他 C# 方法一樣。 其會定義傳回型別、方法名稱和參數。
- 中樞方法最常見的傳回型別是
Task
或Task<TResult>
,其代表非同步中樞作業。 - 方法名稱用於從用戶端呼叫方法。 您可以使用 HubMethodNameAttribute 來加以自訂。
- 參數是選擇性的,但是定義參數時,用戶端應該會提供對應的引數。
引發事件不需要方法,但通常需要。
事件
事件可以依名稱從用戶端訂閱。 伺服器負責引發事件。 Hub
、Hub<T>
、IHubContext<THub>
和 IHubContext<THub, T>
事件會命名,而且最多可以定義 10 個參數。 事件會在伺服器上引發,並由感興趣的用戶端處理。 當用戶端訂閱其中樞連線上的事件時,用戶端會被視為有興趣。 當用戶端呼叫因為其而引動過程以致引發事件的中樞方法時,用戶端可以間接觸發事件。 不過,用戶端無法直接觸發事件,因為這是伺服器的責任。
事件用戶端範圍
您可以從 IClientProxy 執行個體呼叫事件。 您可以從 Clients 類型實作 IHubClients 和 IHubCallerClients 介面。 有多種方式可將範圍限定於特定 IClientProxy
執行個體。 您可以從 Hub.Clients
屬性以下列範圍為目標:
member | 詳細資料 |
---|---|
All |
所有已連線的用戶端 (例如廣播)。 |
AllExcept |
所有已連線的用戶端,不包括指定的連線 (例如已篩選的廣播)。 |
Caller |
觸發方法的已連線用戶端 (例如回應)。 |
Client |
指定的用戶端連線 (單一連線)。 |
Clients |
指定的用戶端連線 (多重連線)。 |
Group |
指定群組內的所有已連線用戶端。 |
GroupExcept |
指定群組內的所有已連線用戶端,不包括指定的連線。 |
Groups |
指定群組內的所有已連線用戶端 (多重群組)。 |
Others |
所有已連線的用戶端,不包括觸發方法的用戶端。 |
OthersInGroup |
指定的群組內的所有已連線用戶端,不包括觸發方法的用戶端。 |
User |
指定使用者的所有已連線用戶端 (單一使用者可以在多個裝置上連線)。 |
Users |
指定使用者的所有已連線用戶端。 |
範例範圍
請考慮下列影像,其可協助您將中樞傳送訊息至目標用戶端的方式視覺化。 您可以展開影像,以改善可讀性。
向所有人廣播
不論已連線的用戶端是否可能屬於的群組,所有已連線的用戶端都會接收此訊息。
隔離的使用者
無論目前使用多少裝置,單一使用者都會收到此訊息。
隔離的群組
只有屬於特定群組的用戶端會收到此訊息。
用戶端和 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
執行個體,則該用戶端可以使用 InvokeAsync 或 SendAsync 擴充功能來呼叫中樞上的方法。 如果中樞方法傳回 Task<TResult>
,則 InvokeAsync<TResult>
的結果為 TResult
類型。 如果中樞方法傳回 Task
,則沒有任何結果。 InvokeAsync
和 SendAsync
都需要中樞方法的名稱,以及零到 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
變化:
- Action
- Action<T>
- Action<T1,T2>
- Action<T1,T2,T3>
- Action<T1,T2,T3,T4>
- Action<T1,T2,T3,T4,T5>
- Action<T1,T2,T3,T4,T5,T6>
- Action<T1,T2,T3,T4,T5,T6,T7>
- Action<T1,T2,T3,T4,T5,T6,T7,T8>
或者,您可以使用非同步處理常式 API,這是 Func<TResult>
,其中 TResult
是 Task
變化:
Func<Task>
Func<T,Task>
Func<T1,T2,Task>
Func<T1,T2,T3,Task>
Func<T1,T2,T3,T4,Task>
Func<T1,T2,T3,T4,T5,Task>
Func<T1,T2,T3,T4,T5,T6,Task>
Func<T1,T2,T3,T4,T5,T6,T7,Task>
Func<T1,T2,T3,T4,T5,T6,T7,T8,Task>
註冊事件處理常式的結果為一個 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
的程式碼,然後開始進行與伺服器的連線。 當其瀏覽至訂單追蹤頁面時,程式碼必須加入訂單的特定群組,變更更新會傳送到其中。 您可以訂閱訂單狀態變更的事件,然後對其進行相應的處理。