ASP.NET Core 用 SignalR でハブを使用する
作成者: Rachel Appel、Kevin Griffin
SignalR Hubs API を使用すると、接続されたクライアントがサーバー上のメソッドを呼び出し、リアルタイム通信を容易にできます。 サーバーはクライアントによって呼び出されるメソッドを定義し、クライアントはサーバーによって呼び出されるメソッドを定義します。 SignalR では、SignalR Hub によって常に仲介される間接的なクライアント間通信も可能になり、個々のクライアント、グループ、または接続されているすべてのクライアントにメッセージを送信できます。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。
SignalR ハブを構成する
SignalR ハブで必要なサービスを登録するには、Program.cs
で AddSignalR を呼び出します。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
SignalR エンドポイントを構成するには、MapHub を呼び出します。これも Program.cs
にあります。
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Note
ASP.NET Core の SignalR サーバー側アセンブリが .NET Core SDK と共にインストールされるようになりました。 詳細については、「SignalR 共有フレームワーク内のアセンブリ」をご覧ください。
ハブを作成して使用する
Hub から継承するクラスを宣言してハブを作成します。 public
メソッドをクラスに追加して、クライアントから呼び出し可能にします。
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Note
ハブは一時的なものです。
- ハブ クラスのプロパティに状態を格納しないでください。 ハブ メソッドの各呼び出しは、新しいハブ インスタンスで実行されます。
- 依存関係の挿入を使用してハブで直接インスタンスを作成しないでください。 アプリケーションの他の場所からクライアントにメッセージを送信するには、
IHubContext
を使用します。 - ハブが存在し続けることに依存する非同期メソッドを呼び出すときは、
await
を使います。 たとえば、Clients.All.SendAsync(...)
などのメソッドは、await
を指定せずに呼び出し、SendAsync
が終了する前にハブ メソッドが完了した場合、失敗する可能性があります。
Context オブジェクト
Hub クラスの Context プロパティには、接続に関する情報が設定される次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
ConnectionId | SignalR によって割り当てられる、接続の一意の ID を取得します。 接続ごとに 1 つの接続 ID があります。 |
UserIdentifier | ユーザー識別子を取得します。 既定では、SignalR は、接続に関連付けられている ClaimsPrincipal からの ClaimTypes.NameIdentifier をユーザー識別子として使用します。 |
User | 現在のユーザーに関連付けられている ClaimsPrincipal を取得します。 |
Items | この接続のスコープ内でデータを共有するために使用できるキーと値のコレクションを取得します。 このコレクションにデータを格納することができ、ハブ メソッドの異なる呼び出しの間も、その接続について維持されます。 |
Features | 接続で使用できる機能のコレクションを取得します。 現時点では、ほとんどのシナリオでこのコレクションは必要ないため、まだ詳細には記載されていません。 |
ConnectionAborted | 接続が中止されたときに通知する CancellationToken を取得します。 |
Hub.Context には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
GetHttpContext | 接続の HttpContext を返します。接続が HTTP 要求に関連付けられていない場合は null を返します。 HTTP 接続の場合は、このメソッドを使用して、HTTP ヘッダーやクエリ文字列などの情報を取得します。 |
Abort | 接続を中止します。 |
Clients オブジェクト
Hub クラスの Clients プロパティには、サーバーとクライアントの間の通信に関する次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
All | 接続されているすべてのクライアントでメソッドを呼び出します |
Caller | ハブ メソッドを呼び出したクライアントでメソッドを呼び出します |
Others | メソッドを呼び出したクライアントを除く、接続されているすべてのクライアントでメソッドを呼び出します |
Hub.Clients には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
AllExcept | 指定した接続を除く、接続されているすべてのクライアントでメソッドを呼び出します |
Client | 接続された特定の 1 つのクライアントでメソッドを呼び出します |
Clients | 接続された特定の複数のクライアントでメソッドを呼び出します |
Group | 指定したグループ内のすべての接続でメソッドを呼び出します |
GroupExcept | 指定した接続を除く、指定したグループ内のすべての接続でメソッドを呼び出します |
Groups | 複数の接続グループでメソッドを呼び出します |
OthersInGroup | ハブ メソッドを呼び出したクライアントを除く、接続のグループでメソッドを呼び出します |
User | 特定のユーザーに関連付けられているすべての接続でメソッドを呼び出します |
Users | 指定したユーザーに関連付けられているすべての接続でメソッドを呼び出します |
SendAsync
メソッドでは、前の表の各プロパティまたはメソッドからオブジェクトが返されます。 SendAsync
メソッドは、呼び出すクライアント メソッドの名前とパラメーターを受け取ります。
Client
メソッドと Caller
メソッドによって返されるオブジェクトにも InvokeAsync
メソッドが含まれています。このメソッドを使用して、クライアントからの結果を待機できます。
クライアントにメッセージを送信する
特定のクライアントに対して呼び出しを行うには、Clients
オブジェクトのプロパティを使用します。 次の例には、3 つのハブ メソッドがあります。
SendMessage
を呼び出すと、Clients.All
を使用して、接続されているすべてのクライアントにメッセージが送信されます。SendMessageToCaller
を呼び出すと、Clients.Caller
を使用して、呼び出し元にメッセージが返送されます。SendMessageToGroup
を呼び出すと、SignalR Users
グループ内のすべてのクライアントにメッセージが送信されます。
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
厳密に型指定されたハブ
SendAsync
を使用する場合の欠点は、呼び出されるクライアント メソッドを指定するのに、文字列が使用されることです。 このため、メソッド名が間違って綴られていたり、クライアントに存在しなくなっていると、コードで実行時エラーが発生します。
SendAsync
を使用する代わりに、Hub<T> を使用して Hub クラスを厳密に型指定する方法もあります。 次の例では、ChatHub
クライアント メソッドが IChatClient
というインターフェイスに抽出されています。
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
このインターフェイスを使用して、前の ChatHub
の例を厳密に型指定するようにリファクタリングすることができます。
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}
Hub<IChatClient>
を使用すると、クライアント メソッドをコンパイル時にチェックできます。 これにより、Hub<T>
では、インターフェイスで定義されたメソッドへのアクセスのみを提供できるため、文字列の使用による問題を回避できます。 厳密に型指定された Hub<T>
を使用すると、SendAsync
は使用できなくなります。
Note
メソッド名からは Async
サフィックスが除かれていません。 クライアント メソッドが .on('MyMethodAsync')
で定義されている場合を除き、MyMethodAsync
を名前として使用しないでください。
クライアント結果
サーバーは、クライアントへの呼び出しに加えて、クライアントからの結果を要求できます。 これには、サーバーは ISingleClientProxy.InvokeAsync
を使用し、クライアントはその .On
ハンドラーから結果を返す必要があります。
サーバーで API を使用するには 2 つの方法があります。1 つ目は、Hub メソッドで Clients
プロパティの Client(...)
または Caller
を呼び出す方法です。
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
2 番目の方法は、IHubContext<T>
のインスタンスで Client(...)
を呼び出す方法です。
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}
厳密に型指定されたハブは、インターフェイス メソッドから値を返すこともできます。
public interface IClient
{
Task<string> GetMessage();
}
public class ChatHub : Hub<IClient>
{
public async Task<string> WaitForMessage(string connectionId)
{
string message = await Clients.Client(connectionId).GetMessage();
return message;
}
}
次に示すように、クライアントは .On(...)
ハンドラーで結果を返します。
.NET クライアント
hubConnection.On("GetMessage", async () =>
{
Console.WriteLine("Enter message:");
var message = await Console.In.ReadLineAsync();
return message;
});
Typescript クライアント
hubConnection.on("GetMessage", async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("message");
}, 100);
});
return promise;
});
Java クライアント
hubConnection.onWithResult("GetMessage", () -> {
return Single.just("message");
});
ハブ メソッドの名前を変更する
既定では、サーバー ハブ メソッドの名前は .NET メソッドの名前です。 特定のメソッドに対するこの既定の動作を変更するには、HubMethodName 属性を使用します。 クライアントでメソッドを呼び出すときは、.NET のメソッド名ではなく、この名前を使用する必要があります。
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
サービスをハブに挿入する
ハブ コンストラクターには DI からのサービスをパラメーターとして指定することができ、それはクラスのプロパティに格納してハブ メソッドで使用できます。
異なるハブ メソッドに複数のサービスを挿入する場合、またはコードを記述する別の方法として、ハブ メソッドで DI からのサービスを受け入れることもできます。 既定では、ハブ メソッドのパラメーターは検査され、可能であれば DI から解決されます。
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message, IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
サービスからのパラメーターの暗黙的な解決が望ましくない場合、DisableImplicitFromServicesParameters を使用して無効にします。
ハブ メソッドで DI から解決されるパラメーターを明示的に指定するには、DisableImplicitFromServicesParameters
オプションを使用し、[FromServices]
属性または DI から解決する必要があるハブ メソッド パラメーターに IFromServiceMetadata
を実装するカスタム属性を使用します。
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
options.DisableImplicitFromServicesParameters = true;
});
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message,
[FromServices] IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
Note
この機能では、IServiceProviderIsServiceを使用します。これは、必要に応じて DI 実装によって実装されます。 アプリの DI コンテナーでこの機能がサポートされていない場合、ハブ メソッドへのサービスの挿入はサポートされません。
依存関係の挿入でのキー付きサービスのサポート
キー付きサービスとは、キーを使用して依存関係の挿入 (DI) サービスを登録および取得する仕組みのことです。 サービスは、AddKeyedSingleton (または AddKeyedScoped
や AddKeyedTransient
) を呼び出して登録することによって関連付けられます。 [FromKeyedServices]
属性でキーを指定して、登録済みサービスにアクセスします。 次のコードは、キー付きサービスの使用方法を示しています。
using Microsoft.AspNetCore.SignalR;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
app.MapRazorPages();
app.MapHub<MyHub>("/myHub");
app.Run();
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
public class MyHub : Hub
{
public void SmallCacheMethod([FromKeyedServices("small")] ICache cache)
{
Console.WriteLine(cache.Get("signalr"));
}
public void BigCacheMethod([FromKeyedServices("big")] ICache cache)
{
Console.WriteLine(cache.Get("signalr"));
}
}
接続のイベントを処理する
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 base.OnDisconnectedAsync(exception);
}
OnDisconnectedAsync で RemoveFromGroupAsync を呼び出す必要はありません。これは自動的に処理されます。
エラーの処理
ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke
メソッドによって JavaScript Promise
が返されます。 クライアントは、返された Promise に catch
ハンドラーをアタッチするか、try
/catch
と async
/await
を使用して例外を処理できます。
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
ハブが例外をスローしても、接続は閉じられません。 既定では、SignalR は、次の例に示すように、一般的なエラー メッセージをクライアントに返します。
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.
予期しない例外には、データベース接続が失敗したときにトリガーされる例外でのデータベース サーバー名など、機密情報が含まれていることがよくあります。 セキュリティ対策として、SignalR では、これらの詳細なエラー メッセージは既定では公開されません。 例外の詳細が抑制される利用の詳細については、「ASP.NET Core のセキュリティに関する考慮事項SignalR」をご覧ください。
例外的な条件をクライアントに伝達する必要がある場合は、HubException クラスを使用します。 ハブ メソッドで HubException
がスローされた場合、SignalRは例外メッセージ全体を変更することなくクライアントに送信します。
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Note
SignalR によってクライアントに送信されるのは、例外の Message
プロパティだけです。 スタック トレースと例外のその他のプロパティは、クライアントでは使用できません。
その他のリソース
作成者: Rachel Appel、Kevin Griffin
SignalR Hubs API を使用すると、接続されたクライアントがサーバー上のメソッドを呼び出し、リアルタイム通信を容易にできます。 サーバーはクライアントによって呼び出されるメソッドを定義し、クライアントはサーバーによって呼び出されるメソッドを定義します。 SignalR では、SignalR Hub によって常に仲介される間接的なクライアント間通信も可能になり、個々のクライアント、グループ、または接続されているすべてのクライアントにメッセージを送信できます。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。
SignalR ハブを構成する
SignalR ハブで必要なサービスを登録するには、Program.cs
で AddSignalR を呼び出します。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
SignalR エンドポイントを構成するには、MapHub を呼び出します。これも Program.cs
にあります。
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Note
ASP.NET Core の SignalR サーバー側アセンブリが .NET Core SDK と共にインストールされるようになりました。 詳細については、「SignalR 共有フレームワーク内のアセンブリ」をご覧ください。
ハブを作成して使用する
Hub から継承するクラスを宣言してハブを作成します。 public
メソッドをクラスに追加して、クライアントから呼び出し可能にします。
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Note
ハブは一時的なものです。
- ハブ クラスのプロパティに状態を格納しないでください。 ハブ メソッドの各呼び出しは、新しいハブ インスタンスで実行されます。
- 依存関係の挿入を使用してハブで直接インスタンスを作成しないでください。 アプリケーションの他の場所からクライアントにメッセージを送信するには、
IHubContext
を使用します。 - ハブが存在し続けることに依存する非同期メソッドを呼び出すときは、
await
を使います。 たとえば、Clients.All.SendAsync(...)
などのメソッドは、await
を指定せずに呼び出し、SendAsync
が終了する前にハブ メソッドが完了した場合、失敗する可能性があります。
Context オブジェクト
Hub クラスの Context プロパティには、接続に関する情報が設定される次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
ConnectionId | SignalR によって割り当てられる、接続の一意の ID を取得します。 接続ごとに 1 つの接続 ID があります。 |
UserIdentifier | ユーザー識別子を取得します。 既定では、SignalR は、接続に関連付けられている ClaimsPrincipal からの ClaimTypes.NameIdentifier をユーザー識別子として使用します。 |
User | 現在のユーザーに関連付けられている ClaimsPrincipal を取得します。 |
Items | この接続のスコープ内でデータを共有するために使用できるキーと値のコレクションを取得します。 このコレクションにデータを格納することができ、ハブ メソッドの異なる呼び出しの間も、その接続について維持されます。 |
Features | 接続で使用できる機能のコレクションを取得します。 現時点では、ほとんどのシナリオでこのコレクションは必要ないため、まだ詳細には記載されていません。 |
ConnectionAborted | 接続が中止されたときに通知する CancellationToken を取得します。 |
Hub.Context には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
GetHttpContext | 接続の HttpContext を返します。接続が HTTP 要求に関連付けられていない場合は null を返します。 HTTP 接続の場合は、このメソッドを使用して、HTTP ヘッダーやクエリ文字列などの情報を取得します。 |
Abort | 接続を中止します。 |
Clients オブジェクト
Hub クラスの Clients プロパティには、サーバーとクライアントの間の通信に関する次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
All | 接続されているすべてのクライアントでメソッドを呼び出します |
Caller | ハブ メソッドを呼び出したクライアントでメソッドを呼び出します |
Others | メソッドを呼び出したクライアントを除く、接続されているすべてのクライアントでメソッドを呼び出します |
Hub.Clients には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
AllExcept | 指定した接続を除く、接続されているすべてのクライアントでメソッドを呼び出します |
Client | 接続された特定の 1 つのクライアントでメソッドを呼び出します |
Clients | 接続された特定の複数のクライアントでメソッドを呼び出します |
Group | 指定したグループ内のすべての接続でメソッドを呼び出します |
GroupExcept | 指定した接続を除く、指定したグループ内のすべての接続でメソッドを呼び出します |
Groups | 複数の接続グループでメソッドを呼び出します |
OthersInGroup | ハブ メソッドを呼び出したクライアントを除く、接続のグループでメソッドを呼び出します |
User | 特定のユーザーに関連付けられているすべての接続でメソッドを呼び出します |
Users | 指定したユーザーに関連付けられているすべての接続でメソッドを呼び出します |
SendAsync
メソッドでは、前の表の各プロパティまたはメソッドからオブジェクトが返されます。 SendAsync
メソッドは、呼び出すクライアント メソッドの名前とパラメーターを受け取ります。
Client
メソッドと Caller
メソッドによって返されるオブジェクトにも InvokeAsync
メソッドが含まれています。このメソッドを使用して、クライアントからの結果を待機できます。
クライアントにメッセージを送信する
特定のクライアントに対して呼び出しを行うには、Clients
オブジェクトのプロパティを使用します。 次の例には、3 つのハブ メソッドがあります。
SendMessage
を呼び出すと、Clients.All
を使用して、接続されているすべてのクライアントにメッセージが送信されます。SendMessageToCaller
を呼び出すと、Clients.Caller
を使用して、呼び出し元にメッセージが返送されます。SendMessageToGroup
を呼び出すと、SignalR Users
グループ内のすべてのクライアントにメッセージが送信されます。
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
厳密に型指定されたハブ
SendAsync
を使用する場合の欠点は、呼び出されるクライアント メソッドを指定するのに、文字列が使用されることです。 このため、メソッド名が間違って綴られていたり、クライアントに存在しなくなっていると、コードで実行時エラーが発生します。
SendAsync
を使用する代わりに、Hub<T> を使用して Hub クラスを厳密に型指定する方法もあります。 次の例では、ChatHub
クライアント メソッドが IChatClient
というインターフェイスに抽出されています。
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
このインターフェイスを使用して、前の ChatHub
の例を厳密に型指定するようにリファクタリングすることができます。
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}
Hub<IChatClient>
を使用すると、クライアント メソッドをコンパイル時にチェックできます。 これにより、Hub<T>
では、インターフェイスで定義されたメソッドへのアクセスのみを提供できるため、文字列の使用による問題を回避できます。 厳密に型指定された Hub<T>
を使用すると、SendAsync
は使用できなくなります。
Note
メソッド名からは Async
サフィックスが除かれていません。 クライアント メソッドが .on('MyMethodAsync')
で定義されている場合を除き、MyMethodAsync
を名前として使用しないでください。
クライアント結果
サーバーは、クライアントへの呼び出しに加えて、クライアントからの結果を要求できます。 これには、サーバーは ISingleClientProxy.InvokeAsync
を使用し、クライアントはその .On
ハンドラーから結果を返す必要があります。
サーバーで API を使用するには 2 つの方法があります。1 つ目は、Hub メソッドで Clients
プロパティの Client(...)
または Caller
を呼び出す方法です。
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
2 番目の方法は、IHubContext<T>
のインスタンスで Client(...)
を呼び出す方法です。
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}
厳密に型指定されたハブは、インターフェイス メソッドから値を返すこともできます。
public interface IClient
{
Task<string> GetMessage();
}
public class ChatHub : Hub<IClient>
{
public async Task<string> WaitForMessage(string connectionId)
{
string message = await Clients.Client(connectionId).GetMessage();
return message;
}
}
次に示すように、クライアントは .On(...)
ハンドラーで結果を返します。
.NET クライアント
hubConnection.On("GetMessage", async () =>
{
Console.WriteLine("Enter message:");
var message = await Console.In.ReadLineAsync();
return message;
});
Typescript クライアント
hubConnection.on("GetMessage", async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("message");
}, 100);
});
return promise;
});
Java クライアント
hubConnection.onWithResult("GetMessage", () -> {
return Single.just("message");
});
ハブ メソッドの名前を変更する
既定では、サーバー ハブ メソッドの名前は .NET メソッドの名前です。 特定のメソッドに対するこの既定の動作を変更するには、HubMethodName 属性を使用します。 クライアントでメソッドを呼び出すときは、.NET のメソッド名ではなく、この名前を使用する必要があります。
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
サービスをハブに挿入する
ハブ コンストラクターには DI からのサービスをパラメーターとして指定することができ、それはクラスのプロパティに格納してハブ メソッドで使用できます。
異なるハブ メソッドに複数のサービスを挿入する場合、またはコードを記述する別の方法として、ハブ メソッドで DI からのサービスを受け入れることもできます。 既定では、ハブ メソッドのパラメーターは検査され、可能であれば DI から解決されます。
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message, IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
サービスからのパラメーターの暗黙的な解決が望ましくない場合、DisableImplicitFromServicesParameters を使用して無効にします。
ハブ メソッドで DI から解決されるパラメーターを明示的に指定するには、DisableImplicitFromServicesParameters
オプションを使用し、[FromServices]
属性または DI から解決する必要があるハブ メソッド パラメーターに IFromServiceMetadata
を実装するカスタム属性を使用します。
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
options.DisableImplicitFromServicesParameters = true;
});
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message,
[FromServices] IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
Note
この機能では、IServiceProviderIsServiceを使用します。これは、必要に応じて DI 実装によって実装されます。 アプリの DI コンテナーでこの機能がサポートされていない場合、ハブ メソッドへのサービスの挿入はサポートされません。
接続のイベントを処理する
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 base.OnDisconnectedAsync(exception);
}
OnDisconnectedAsync で RemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。
エラーの処理
ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke
メソッドによって JavaScript Promise
が返されます。 クライアントは、返された Promise に catch
ハンドラーをアタッチするか、try
/catch
と async
/await
を使用して例外を処理できます。
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
ハブが例外をスローしても、接続は閉じられません。 既定では、SignalR は、次の例に示すように、一般的なエラー メッセージをクライアントに返します。
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.
予期しない例外には、データベース接続が失敗したときにトリガーされる例外でのデータベース サーバー名など、機密情報が含まれていることがよくあります。 セキュリティ対策として、SignalR では、これらの詳細なエラー メッセージは既定では公開されません。 例外の詳細が抑制される利用の詳細については、「ASP.NET Core のセキュリティに関する考慮事項SignalR」をご覧ください。
例外的な条件をクライアントに伝達する必要がある場合は、HubException クラスを使用します。 ハブ メソッドで HubException
がスローされた場合、SignalRは例外メッセージ全体を変更することなくクライアントに送信します。
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Note
SignalR によってクライアントに送信されるのは、例外の Message
プロパティだけです。 スタック トレースと例外のその他のプロパティは、クライアントでは使用できません。
その他のリソース
作成者: Rachel Appel、Kevin Griffin
SignalR Hubs API を使用すると、接続されたクライアントがサーバー上のメソッドを呼び出し、リアルタイム通信を容易にできます。 サーバーはクライアントによって呼び出されるメソッドを定義し、クライアントはサーバーによって呼び出されるメソッドを定義します。 SignalR では、SignalR Hub によって常に仲介される間接的なクライアント間通信も可能になり、個々のクライアント、グループ、または接続されているすべてのクライアントにメッセージを送信できます。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。
SignalR ハブを構成する
SignalR ハブで必要なサービスを登録するには、Program.cs
で AddSignalR を呼び出します。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
SignalR エンドポイントを構成するには、MapHub を呼び出します。これも Program.cs
にあります。
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Note
ASP.NET Core の SignalR サーバー側アセンブリが .NET Core SDK と共にインストールされるようになりました。 詳細については、「SignalR 共有フレームワーク内のアセンブリ」をご覧ください。
ハブを作成して使用する
Hub から継承するクラスを宣言してハブを作成します。 public
メソッドをクラスに追加して、クライアントから呼び出し可能にします。
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Note
ハブは一時的なものです。
- ハブ クラスのプロパティに状態を格納しないでください。 ハブ メソッドの各呼び出しは、新しいハブ インスタンスで実行されます。
- 依存関係の挿入を使用してハブで直接インスタンスを作成しないでください。 アプリケーションの他の場所からクライアントにメッセージを送信するには、
IHubContext
を使用します。 - ハブが存在し続けることに依存する非同期メソッドを呼び出すときは、
await
を使います。 たとえば、Clients.All.SendAsync(...)
などのメソッドは、await
を指定せずに呼び出し、SendAsync
が終了する前にハブ メソッドが完了した場合、失敗する可能性があります。
Context オブジェクト
Hub クラスの Context プロパティには、接続に関する情報が設定される次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
ConnectionId | SignalR によって割り当てられる、接続の一意の ID を取得します。 接続ごとに 1 つの接続 ID があります。 |
UserIdentifier | ユーザー識別子を取得します。 既定では、SignalR は、接続に関連付けられている ClaimsPrincipal からの ClaimTypes.NameIdentifier をユーザー識別子として使用します。 |
User | 現在のユーザーに関連付けられている ClaimsPrincipal を取得します。 |
Items | この接続のスコープ内でデータを共有するために使用できるキーと値のコレクションを取得します。 このコレクションにデータを格納することができ、ハブ メソッドの異なる呼び出しの間も、その接続について維持されます。 |
Features | 接続で使用できる機能のコレクションを取得します。 現時点では、ほとんどのシナリオでこのコレクションは必要ないため、まだ詳細には記載されていません。 |
ConnectionAborted | 接続が中止されたときに通知する CancellationToken を取得します。 |
Hub.Context には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
GetHttpContext | 接続の HttpContext を返します。接続が HTTP 要求に関連付けられていない場合は null を返します。 HTTP 接続の場合は、このメソッドを使用して、HTTP ヘッダーやクエリ文字列などの情報を取得します。 |
Abort | 接続を中止します。 |
Clients オブジェクト
Hub クラスの Clients プロパティには、サーバーとクライアントの間の通信に関する次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
All | 接続されているすべてのクライアントでメソッドを呼び出します |
Caller | ハブ メソッドを呼び出したクライアントでメソッドを呼び出します |
Others | メソッドを呼び出したクライアントを除く、接続されているすべてのクライアントでメソッドを呼び出します |
Hub.Clients には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
AllExcept | 指定した接続を除く、接続されているすべてのクライアントでメソッドを呼び出します |
Client | 接続された特定の 1 つのクライアントでメソッドを呼び出します |
Clients | 接続された特定の複数のクライアントでメソッドを呼び出します |
Group | 指定したグループ内のすべての接続でメソッドを呼び出します |
GroupExcept | 指定した接続を除く、指定したグループ内のすべての接続でメソッドを呼び出します |
Groups | 複数の接続グループでメソッドを呼び出します |
OthersInGroup | ハブ メソッドを呼び出したクライアントを除く、接続のグループでメソッドを呼び出します |
User | 特定のユーザーに関連付けられているすべての接続でメソッドを呼び出します |
Users | 指定したユーザーに関連付けられているすべての接続でメソッドを呼び出します |
SendAsync
メソッドでは、前の表の各プロパティまたはメソッドからオブジェクトが返されます。 SendAsync
メソッドは、呼び出すクライアント メソッドの名前とパラメーターを受け取ります。
クライアントにメッセージを送信する
特定のクライアントに対して呼び出しを行うには、Clients
オブジェクトのプロパティを使用します。 次の例には、3 つのハブ メソッドがあります。
SendMessage
を呼び出すと、Clients.All
を使用して、接続されているすべてのクライアントにメッセージが送信されます。SendMessageToCaller
を呼び出すと、Clients.Caller
を使用して、呼び出し元にメッセージが返送されます。SendMessageToGroup
を呼び出すと、SignalR Users
グループ内のすべてのクライアントにメッセージが送信されます。
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
厳密に型指定されたハブ
SendAsync
を使用する場合の欠点は、呼び出されるクライアント メソッドを指定するのに、文字列が使用されることです。 このため、メソッド名が間違って綴られていたり、クライアントに存在しなくなっていると、コードで実行時エラーが発生します。
SendAsync
を使用する代わりに、Hub<T> を使用して Hub クラスを厳密に型指定する方法もあります。 次の例では、ChatHub
クライアント メソッドが IChatClient
というインターフェイスに抽出されています。
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
このインターフェイスを使用して、前の ChatHub
の例を厳密に型指定するようにリファクタリングすることができます。
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}
Hub<IChatClient>
を使用すると、クライアント メソッドをコンパイル時にチェックできます。 これにより、Hub<T>
では、インターフェイスで定義されたメソッドへのアクセスのみを提供できるため、文字列の使用による問題を回避できます。 厳密に型指定された Hub<T>
を使用すると、SendAsync
は使用できなくなります。
Note
メソッド名からは Async
サフィックスが除かれていません。 クライアント メソッドが .on('MyMethodAsync')
で定義されている場合を除き、MyMethodAsync
を名前として使用しないでください。
ハブ メソッドの名前を変更する
既定では、サーバー ハブ メソッドの名前は .NET メソッドの名前です。 特定のメソッドに対するこの既定の動作を変更するには、HubMethodName 属性を使用します。 クライアントでメソッドを呼び出すときは、.NET のメソッド名ではなく、この名前を使用する必要があります。
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, 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 base.OnDisconnectedAsync(exception);
}
OnDisconnectedAsync で RemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。
エラーの処理
ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke
メソッドによって JavaScript Promise
が返されます。 クライアントは、返された Promise に catch
ハンドラーをアタッチするか、try
/catch
と async
/await
を使用して例外を処理できます。
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
ハブが例外をスローしても、接続は閉じられません。 既定では、SignalR は、次の例に示すように、一般的なエラー メッセージをクライアントに返します。
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.
予期しない例外には、データベース接続が失敗したときにトリガーされる例外でのデータベース サーバー名など、機密情報が含まれていることがよくあります。 セキュリティ対策として、SignalR では、これらの詳細なエラー メッセージは既定では公開されません。 例外の詳細が抑制される利用の詳細については、「ASP.NET Core のセキュリティに関する考慮事項SignalR」をご覧ください。
例外的な条件をクライアントに伝達する必要がある場合は、HubException クラスを使用します。 ハブ メソッドで HubException
がスローされた場合、SignalRは例外メッセージ全体を変更することなくクライアントに送信します。
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Note
SignalR によってクライアントに送信されるのは、例外の Message
プロパティだけです。 スタック トレースと例外のその他のプロパティは、クライアントでは使用できません。
その他のリソース
作成者: Rachel Appel、Kevin Griffin
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
SignalR ハブとは
SignalR Hubs API を使用すると、接続されたクライアントがサーバー上のメソッドを呼び出し、リアルタイム通信を容易にできます。 サーバーはクライアントによって呼び出されるメソッドを定義し、クライアントはサーバーによって呼び出されるメソッドを定義します。 SignalR では、SignalR Hub によって常に仲介される間接的なクライアント間通信も可能になり、個々のクライアント、グループ、または接続されているすべてのクライアントにメッセージを送信できます。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。
SignalR ハブを構成する
SignalR ミドルウェアに必要ないくつかのサービスは、AddSignalR を呼び出すと構成されます。
services.AddSignalR();
ASP.NET Core アプリに SignalR 機能を追加するときは、Startup.Configure
メソッドの UseEndpoints コールバックで MapHub を呼び出して SignalR ルートをセットアップします。
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chathub");
});
Note
ASP.NET Core の SignalR サーバー側アセンブリが .NET Core SDK と共にインストールされるようになりました。 詳細については、「SignalR 共有フレームワーク内のアセンブリ」をご覧ください。
ハブを作成して使用する
Hub から継承するクラスを宣言してハブを作成し、それにパブリック メソッドを追加します。 クライアントは、public
として定義されているメソッドを呼び出すことができます。
public class ChatHub : Hub
{
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
C# のメソッドの場合と同様に、複合型や配列など、戻り値の型とパラメーターを指定できます。 パラメーターと戻り値の複合オブジェクトおよび配列のシリアル化と逆シリアル化は、SignalR によって処理されます。
Note
ハブは一時的なものです。
- ハブ クラスのプロパティに状態を格納しないでください。 ハブ メソッドのすべての呼び出しは、新しいハブ インスタンスで実行されます。
- 依存関係の挿入を使用してハブで直接インスタンスを作成しないでください。 アプリケーションの他の場所からクライアントにメッセージを送信するには、
IHubContext
を使用します。 - ハブが存在し続けることに依存する非同期メソッドを呼び出すときは、
await
を使います。 たとえば、Clients.All.SendAsync(...)
などのメソッドは、await
を指定せずに呼び出し、SendAsync
が終了する前にハブ メソッドが完了した場合、失敗する可能性があります。
Context オブジェクト
Hub クラスの Context プロパティには、接続に関する情報が設定される次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
ConnectionId | SignalR によって割り当てられる、接続の一意の ID を取得します。 接続ごとに 1 つの接続 ID があります。 |
UserIdentifier | ユーザー識別子を取得します。 既定では、SignalR は、接続に関連付けられている ClaimsPrincipal からの ClaimTypes.NameIdentifier をユーザー識別子として使用します。 |
User | 現在のユーザーに関連付けられている ClaimsPrincipal を取得します。 |
Items | この接続のスコープ内でデータを共有するために使用できるキーと値のコレクションを取得します。 このコレクションにデータを格納することができ、ハブ メソッドの異なる呼び出しの間も、その接続について維持されます。 |
Features | 接続で使用できる機能のコレクションを取得します。 現時点では、ほとんどのシナリオでこのコレクションは必要ないため、まだ詳細には記載されていません。 |
ConnectionAborted | 接続が中止されたときに通知する CancellationToken を取得します。 |
Hub.Context には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
GetHttpContext | 接続の HttpContext を返します。接続が HTTP 要求に関連付けられていない場合は null を返します。 HTTP 接続の場合は、このメソッドを使って、HTTP ヘッダーやクエリ文字列などの情報を取得できます。 |
Abort | 接続を中止します。 |
Clients オブジェクト
Hub クラスの Clients プロパティには、サーバーとクライアントの間の通信に関する次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
All | 接続されているすべてのクライアントでメソッドを呼び出します |
Caller | ハブ メソッドを呼び出したクライアントでメソッドを呼び出します |
Others | メソッドを呼び出したクライアントを除く、接続されているすべてのクライアントでメソッドを呼び出します |
Hub.Clients には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
AllExcept | 指定した接続を除く、接続されているすべてのクライアントでメソッドを呼び出します |
Client | 接続された特定の 1 つのクライアントでメソッドを呼び出します |
Clients | 接続された特定の複数のクライアントでメソッドを呼び出します |
Group | 指定したグループ内のすべての接続でメソッドを呼び出します |
GroupExcept | 指定した接続を除く、指定したグループ内のすべての接続でメソッドを呼び出します |
Groups | 複数の接続グループでメソッドを呼び出します |
OthersInGroup | ハブ メソッドを呼び出したクライアントを除く、接続のグループでメソッドを呼び出します |
User | 特定のユーザーに関連付けられているすべての接続でメソッドを呼び出します |
Users | 指定したユーザーに関連付けられているすべての接続でメソッドを呼び出します |
SendAsync
メソッドでは、前の表の各プロパティまたはメソッドからオブジェクトが返されます。 SendAsync
メソッドを使用すると、呼び出すクライアント メソッドの名前とパラメーターを指定できます。
クライアントにメッセージを送信する
特定のクライアントに対して呼び出しを行うには、Clients
オブジェクトのプロパティを使用します。 次の例には、3 つのハブ メソッドがあります。
SendMessage
を呼び出すと、Clients.All
を使用して、接続されているすべてのクライアントにメッセージが送信されます。SendMessageToCaller
を呼び出すと、Clients.Caller
を使用して、呼び出し元にメッセージが返送されます。SendMessageToGroup
を呼び出すと、SignalR Users
グループ内のすべてのクライアントにメッセージが送信されます。
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string user, string message)
{
return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}
厳密に型指定されたハブ
SendAsync
を使用する場合の欠点は、呼び出されるクライアント メソッドを指定するのに、マジック文字列が使用されることです。 このため、メソッド名が間違って綴られていたり、クライアントに存在しなくなっていると、コードで実行時エラーが発生します。
SendAsync
を使用する代わりに、Hub<T> を使用して Hub を厳密に型指定する方法もあります。 次の例では、ChatHub
クライアント メソッドが IChatClient
というインターフェイスに抽出されています。
public interface IChatClient
{
Task ReceiveMessage(string user, 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 user, string message)
{
return Clients.Caller.ReceiveMessage(user, message);
}
}
Hub<IChatClient>
を使用すると、クライアント メソッドをコンパイル時にチェックできます。 これにより、Hub<T>
では、インターフェイスで定義されたメソッドへのアクセスのみを提供できるため、マジック文字列の使用による問題を回避できます。
厳密に型指定された Hub<T>
を使用すると、SendAsync
は使用できなくなります。 インターフェイスで定義されているどのメソッドも、引き続き非同期として定義できます。 実際、これらの各メソッドからは Task
を返す必要があります。 これはインターフェイスであるため、async
キーワードを使用しないでください。 次に例を示します。
public interface IClient
{
Task ClientMethod();
}
Note
メソッド名からは Async
サフィックスが除かれていません。 クライアント メソッドが .on('MyMethodAsync')
で定義されていない場合は、名前として MyMethodAsync
を使用しないでください。
ハブ メソッドの名前を変更する
既定では、サーバー ハブ メソッドの名前は .NET メソッドの名前です。 ただし、HubMethodName 属性を使ってこの既定値を変更し、メソッドの名前を手動で指定することができます。 クライアントでメソッドを呼び出すときは、.NET のメソッド名ではなく、この名前を使用する必要があります。
[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", user, 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 Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
await base.OnDisconnectedAsync(exception);
}
OnDisconnectedAsync で RemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。
警告
セキュリティ警告: ConnectionId
を公開すると、サーバーまたはクライアントのバージョンが SignalR ASP.NET Core 2.2 以前である場合、偽装される可能性があります。
エラーの処理
ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke
メソッドによって JavaScript Promise
が返されます。 クライアントが catch
を使用してハンドラーが Promise にアタッチされたエラーを受け取ると、それが呼び出されて、JavaScript の Error
オブジェクトとして渡されます。
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 では、これらの詳細なエラー メッセージは既定では公開されません。 例外の詳細が抑制される利用の詳細については、「ASP.NET Core のセキュリティに関する考慮事項SignalR」をご覧ください。
例外の状態をクライアントに伝える必要が "ある" 場合は、HubException クラスを使用できます。 ハブ メソッドから HubException
をスローすると、SignalR によってメッセージ全体が変更されずにクライアントに送信されます。
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
Note
SignalR によってクライアントに送信されるのは、例外の Message
プロパティだけです。 スタック トレースと例外のその他のプロパティは、クライアントでは使用できません。
その他のリソース
作成者: Rachel Appel、Kevin Griffin
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
SignalR ハブとは
SignalR Hubs API を使用すると、接続されたクライアントがサーバー上のメソッドを呼び出し、リアルタイム通信を容易にできます。 サーバーはクライアントによって呼び出されるメソッドを定義し、クライアントはサーバーによって呼び出されるメソッドを定義します。 SignalR では、SignalR Hub によって常に仲介される間接的なクライアント間通信も可能になり、個々のクライアント、グループ、または接続されているすべてのクライアントにメッセージを送信できます。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。
SignalR ハブを構成する
SignalR ミドルウェアに必要ないくつかのサービスは、AddSignalR を呼び出すと構成されます。
services.AddSignalR();
ASP.NET Core アプリに SignalR 機能を追加するときは、Startup.Configure
メソッドで UseSignalR を呼び出して SignalR ルートをセットアップします。
app.UseSignalR(route =>
{
route.MapHub<ChatHub>("/chathub");
});
ハブを作成して使用する
Hub から継承するクラスを宣言してハブを作成し、それにパブリック メソッドを追加します。 クライアントは、public
として定義されているメソッドを呼び出すことができます。
public class ChatHub : Hub
{
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
C# のメソッドの場合と同様に、複合型や配列など、戻り値の型とパラメーターを指定できます。 パラメーターと戻り値の複合オブジェクトおよび配列のシリアル化と逆シリアル化は、SignalR によって処理されます。
Note
ハブは一時的なものです。
- ハブ クラスのプロパティに状態を格納しないでください。 ハブ メソッドのすべての呼び出しは、新しいハブ インスタンスで実行されます。
- 依存関係の挿入を使用してハブで直接インスタンスを作成しないでください。 アプリケーションの他の場所からクライアントにメッセージを送信するには、
IHubContext
を使用します。 - ハブが存在し続けることに依存する非同期メソッドを呼び出すときは、
await
を使います。 たとえば、Clients.All.SendAsync(...)
などのメソッドは、await
を指定せずに呼び出し、SendAsync
が終了する前にハブ メソッドが完了した場合、失敗する可能性があります。
Context オブジェクト
Hub クラスの Context プロパティには、接続に関する情報が設定される次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
ConnectionId | SignalR によって割り当てられる、接続の一意の ID を取得します。 接続ごとに 1 つの接続 ID があります。 |
UserIdentifier | ユーザー識別子を取得します。 既定では、SignalR は、接続に関連付けられている ClaimsPrincipal からの ClaimTypes.NameIdentifier をユーザー識別子として使用します。 |
User | 現在のユーザーに関連付けられている ClaimsPrincipal を取得します。 |
Items | この接続のスコープ内でデータを共有するために使用できるキーと値のコレクションを取得します。 このコレクションにデータを格納することができ、ハブ メソッドの異なる呼び出しの間も、その接続について維持されます。 |
Features | 接続で使用できる機能のコレクションを取得します。 現時点では、ほとんどのシナリオでこのコレクションは必要ないため、まだ詳細には記載されていません。 |
ConnectionAborted | 接続が中止されたときに通知する CancellationToken を取得します。 |
Hub.Context には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
GetHttpContext | 接続の HttpContext を返します。接続が HTTP 要求に関連付けられていない場合は null を返します。 HTTP 接続の場合は、このメソッドを使って、HTTP ヘッダーやクエリ文字列などの情報を取得できます。 |
Abort | 接続を中止します。 |
Clients オブジェクト
Hub クラスの Clients プロパティには、サーバーとクライアントの間の通信に関する次のプロパティが含まれます。
プロパティ | 説明 |
---|---|
All | 接続されているすべてのクライアントでメソッドを呼び出します |
Caller | ハブ メソッドを呼び出したクライアントでメソッドを呼び出します |
Others | メソッドを呼び出したクライアントを除く、接続されているすべてのクライアントでメソッドを呼び出します |
Hub.Clients には、次のメソッドも含まれます。
メソッド | 説明 |
---|---|
AllExcept | 指定した接続を除く、接続されているすべてのクライアントでメソッドを呼び出します |
Client | 接続された特定の 1 つのクライアントでメソッドを呼び出します |
Clients | 接続された特定の複数のクライアントでメソッドを呼び出します |
Group | 指定したグループ内のすべての接続でメソッドを呼び出します |
GroupExcept | 指定した接続を除く、指定したグループ内のすべての接続でメソッドを呼び出します |
Groups | 複数の接続グループでメソッドを呼び出します |
OthersInGroup | ハブ メソッドを呼び出したクライアントを除く、接続のグループでメソッドを呼び出します |
User | 特定のユーザーに関連付けられているすべての接続でメソッドを呼び出します |
Users | 指定したユーザーに関連付けられているすべての接続でメソッドを呼び出します |
SendAsync
メソッドでは、前の表の各プロパティまたはメソッドからオブジェクトが返されます。 SendAsync
メソッドを使用すると、呼び出すクライアント メソッドの名前とパラメーターを指定できます。
クライアントにメッセージを送信する
特定のクライアントに対して呼び出しを行うには、Clients
オブジェクトのプロパティを使用します。 次の例には、3 つのハブ メソッドがあります。
SendMessage
を呼び出すと、Clients.All
を使用して、接続されているすべてのクライアントにメッセージが送信されます。SendMessageToCaller
を呼び出すと、Clients.Caller
を使用して、呼び出し元にメッセージが返送されます。SendMessageToGroup
を呼び出すと、SignalR Users
グループ内のすべてのクライアントにメッセージが送信されます。
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string user, string message)
{
return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}
厳密に型指定されたハブ
SendAsync
を使用する場合の欠点は、呼び出されるクライアント メソッドを指定するのに、マジック文字列が使用されることです。 このため、メソッド名が間違って綴られていたり、クライアントに存在しなくなっていると、コードで実行時エラーが発生します。
SendAsync
を使用する代わりに、Hub<T> を使用して Hub を厳密に型指定する方法もあります。 次の例では、ChatHub
クライアント メソッドが IChatClient
というインターフェイスに抽出されています。
public interface IChatClient
{
Task ReceiveMessage(string user, 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 user, string message)
{
return Clients.Caller.ReceiveMessage(user, message);
}
}
Hub<IChatClient>
を使用すると、クライアント メソッドをコンパイル時にチェックできます。 これにより、Hub<T>
では、インターフェイスで定義されたメソッドへのアクセスのみを提供できるため、マジック文字列の使用による問題を回避できます。
厳密に型指定された Hub<T>
を使用すると、SendAsync
は使用できなくなります。 インターフェイスで定義されているどのメソッドも、引き続き非同期として定義できます。 実際、これらの各メソッドからは Task
を返す必要があります。 これはインターフェイスであるため、async
キーワードを使用しないでください。 次に例を示します。
public interface IClient
{
Task ClientMethod();
}
Note
メソッド名からは Async
サフィックスが除かれていません。 クライアント メソッドが .on('MyMethodAsync')
で定義されていない場合は、名前として MyMethodAsync
を使用しないでください。
ハブ メソッドの名前を変更する
既定では、サーバー ハブ メソッドの名前は .NET メソッドの名前です。 ただし、HubMethodName 属性を使ってこの既定値を変更し、メソッドの名前を手動で指定することができます。 クライアントでメソッドを呼び出すときは、.NET のメソッド名ではなく、この名前を使用する必要があります。
[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", user, 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 Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
await base.OnDisconnectedAsync(exception);
}
OnDisconnectedAsync で RemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。
警告
セキュリティ警告: ConnectionId
を公開すると、サーバーまたはクライアントのバージョンが SignalR ASP.NET Core 2.2 以前である場合、偽装される可能性があります。
エラーの処理
ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke
メソッドによって JavaScript Promise
が返されます。 クライアントが catch
を使用してハンドラーが Promise にアタッチされたエラーを受け取ると、それが呼び出されて、JavaScript の Error
オブジェクトとして渡されます。
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 では、これらの詳細なエラー メッセージは既定では公開されません。 例外の詳細が抑制される利用の詳細については、「ASP.NET Core のセキュリティに関する考慮事項SignalR」をご覧ください。
例外の状態をクライアントに伝える必要が "ある" 場合は、HubException クラスを使用できます。 ハブ メソッドから HubException
をスローすると、SignalR によってメッセージ全体が変更されずにクライアントに送信されます。
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
Note
SignalR によってクライアントに送信されるのは、例外の Message
プロパティだけです。 スタック トレースと例外のその他のプロパティは、クライアントでは使用できません。
その他のリソース
ASP.NET Core