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
は "ルート" を介して公開されます。 たとえば、OrdersHub
の実装を表すために https://www.contoso-pizza.com/hubs/orders
ルートを使用できます。 作成者はさまざまなハブ API を使用して、メソッドとイベントを定義できます。
ハブでメソッドを公開するための 2 つのモダリティがあります。 次の型のサブクラスを作成し、メソッドを記述します。
例 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
への呼び出しとして表されます。 notification
インスタンスに応じて、NotificationReceived
イベントが発生します。
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
プロパティから次のスコープを対象にできます。
メンバー | 詳細 |
---|---|
All |
すべての接続済みクライアント (ブロードキャストなど)。 |
AllExcept |
指定した接続を除くすべての接続済みクライアント (フィルター処理されたブロードキャストなど)。 |
Caller |
メソッドをトリガーした接続済みクライアント (エコーなど)。 |
Client |
指定されたクライアント接続 (単一接続)。 |
Clients |
指定されたクライアント接続 (複数接続)。 |
Group |
指定したグループ内のすべての接続済みクライアント。 |
GroupExcept |
指定したグループ内のすべての接続済みクライアント (指定した接続を除く)。 |
Groups |
指定したグループ内のすべての接続済みクライアント (複数グループ)。 |
Others |
メソッドをトリガーしたクライアントを除くすべての接続済みクライアント。 |
OthersInGroup |
メソッドをトリガーしたクライアントを除く、指定したグループ内のすべての接続済みクライアント。 |
User |
指定した 1 人のユーザーに対するすべての接続済みクライアント (1 人のユーザーが複数のデバイスに接続できます)。 |
Users |
指定した複数のユーザーに対するすべての接続済みクライアント。 |
スコープの例
ハブがターゲット クライアントにメッセージを送信する方法をイメージするのに役立ついくつかの画像に注目してみましょう。 見やすくするために画像を拡大できます。
全員にブロードキャストする
すべての接続済みクライアントは、そのグループに属しているかどうかに関わらずこのメッセージを受信します。
分離ユーザー
現在使用しているデバイスの数に関わらず、1 人のユーザーがこのメッセージを受信します。
分離グループ
このメッセージを受信できるのは、特定のグループに属しているクライアントのみです。
クライアントと 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
はどちらも、ハブ メソッドの名前と、0 から 10 個のパラメーターが必要です。
- InvokeAsync: 指定したメソッド名とオプションの引数を使用して、サーバー上のハブ メソッドを呼び出します。
- SendAsync: 指定したメソッド名とオプションの引数を使用して、サーバー上のハブ メソッドを呼び出します。 このメソッドは、受信側からの応答を "待機しません"。
ハブ メソッド呼び出しの例
SendNotificationAsync
で前の Consumer
クラスにメソッドを追加すると、SendNotificationAsync
が _hubConnection
にデリゲートされ、Notification
インスタンスに応じて、サーバーのハブ上で NotifyAll
メソッドが呼び出されます。
public Task SendNotificationAsync(string text) =>
_hubConnection.InvokeAsync(
"NotifyAll", new Notification(text, DateTime.UtcNow));
イベントを処理する
イベントを処理するには、ハンドラーに HubConnection
インスタンスを登録します。 ハブ メソッドの名前と 0 から 8 個のパラメーターを指定して、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>
または、Func<TResult>
である非同期ハンドラー API を使用できます。ここで、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.
}
OnNotificationReceivedAsync
メソッドは、サーバーのハブ インスタンスが "NotificationReceived"
イベントを発生すると呼び出されます。
Contoso Pizza ライブ注文の更新
Web アプリケーション用のサーバー コードでは、Hub
を実装し、ルートをクライアントに公開する必要があります。 Hub
では、注文オブジェクトの一意識別子を使用して、追跡用のグループを作成できます。 そのようにすると、注文状態の変更の更新すべてをこのグループ内で通信できます。
Contoso Pizza アプリケーションが Blazor WebAssembly アプリであることを示すため、クライアント コードも更新する必要があります。 JavaScript SDK か .NET クライアント SDK を使用できます。 次に、クライアント側のポーリング機能を、HubConnection
をビルドするコードに置き換え、サーバーへの接続を開始します。 ユーザーが注文追跡ページに移動する際に、コードは変更の更新が送信される注文の特定のグループに参加する必要があります。 注文状態の変更に関するイベントをサブスクライブし、それに応じて処理します。