ASP.NET Core 用 SignalR でハブを使用する

作成者: Rachel AppelKevin Griffin

SignalR Hubs API を使用すると、接続されているクライアントはメソッドをサーバーから呼び出すことができます。 サーバーはクライアントから呼び出されるメソッドを定義し、クライアントはサーバーから呼び出されるメソッドを定義します。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。

SignalR ハブを構成する

SignalR ハブで必要なサービスを登録するには、Program.csAddSignalR を呼び出します。

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 (または AddKeyedScopedAddKeyedTransient) を呼び出して登録することによって関連付けられます。 [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 には、接続の管理と追跡のために、仮想メソッド OnConnectedAsyncOnDisconnectedAsync が用意されています。 クライアントがハブに接続するときにアクションを実行するには (グループへの追加など)、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);
}

OnDisconnectedAsyncRemoveFromGroupAsync を呼び出す必要はありません。これは自動的に処理されます。

エラーの処理

ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke メソッドによって JavaScript Promise が返されます。 クライアントは、返された Promise に catch ハンドラーをアタッチするか、try/catchasync/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 AppelKevin Griffin

SignalR Hubs API を使用すると、接続されているクライアントはメソッドをサーバーから呼び出すことができます。 サーバーはクライアントから呼び出されるメソッドを定義し、クライアントはサーバーから呼び出されるメソッドを定義します。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。

SignalR ハブを構成する

SignalR ハブで必要なサービスを登録するには、Program.csAddSignalR を呼び出します。

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 には、接続の管理と追跡のために、仮想メソッド OnConnectedAsyncOnDisconnectedAsync が用意されています。 クライアントがハブに接続するときにアクションを実行するには (グループへの追加など)、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);
}

OnDisconnectedAsyncRemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。

エラーの処理

ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke メソッドによって JavaScript Promise が返されます。 クライアントは、返された Promise に catch ハンドラーをアタッチするか、try/catchasync/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 AppelKevin Griffin

SignalR Hubs API を使用すると、接続されているクライアントはメソッドをサーバーから呼び出すことができます。 サーバーはクライアントから呼び出されるメソッドを定義し、クライアントはサーバーから呼び出されるメソッドを定義します。 SignalR は、リアルタイムのクライアントとサーバー間およびサーバーからクライアント間の通信を可能にするために必要なすべての処理を行います。

SignalR ハブを構成する

SignalR ハブで必要なサービスを登録するには、Program.csAddSignalR を呼び出します。

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 には、接続の管理と追跡のために、仮想メソッド OnConnectedAsyncOnDisconnectedAsync が用意されています。 クライアントがハブに接続するときにアクションを実行するには (グループへの追加など)、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);
}

OnDisconnectedAsyncRemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。

エラーの処理

ハブ メソッドでスローされた例外は、メソッドを呼び出したクライアントに送信されます。 JavaScript クライアントでは、invoke メソッドによって JavaScript Promise が返されます。 クライアントは、返された Promise に catch ハンドラーをアタッチするか、try/catchasync/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 AppelKevin Griffin

サンプル コードを表示またはダウンロードする (ダウンロード方法)

SignalR ハブとは

SignalR Hubs API を使用すると、接続されているクライアント上のメソッドをサーバーから呼び出すことができます。 サーバーのコードでは、クライアントによって呼び出されるメソッドを定義します。 クライアントのコードでは、サーバーから呼び出されるメソッドを定義します。 クライアントからサーバーおよびサーバーからクライアントへのリアルタイム通信を可能にするバックグラウンドの処理はすべて、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 には、接続の管理と追跡のために、仮想メソッド OnConnectedAsyncOnDisconnectedAsync が用意されています。 クライアントがハブに接続するときにアクションを実行するには (グループへの追加など)、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);
}

OnDisconnectedAsyncRemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。

警告

セキュリティ警告: 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 AppelKevin Griffin

サンプル コードを表示またはダウンロードする (ダウンロード方法)

SignalR ハブとは

SignalR Hubs API を使用すると、接続されているクライアント上のメソッドをサーバーから呼び出すことができます。 サーバーのコードでは、クライアントによって呼び出されるメソッドを定義します。 クライアントのコードでは、サーバーから呼び出されるメソッドを定義します。 クライアントからサーバーおよびサーバーからクライアントへのリアルタイム通信を可能にするバックグラウンドの処理はすべて、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 には、接続の管理と追跡のために、仮想メソッド OnConnectedAsyncOnDisconnectedAsync が用意されています。 クライアントがハブに接続するときにアクションを実行するには (グループへの追加など)、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);
}

OnDisconnectedAsyncRemoveFromGroupAsync を呼び出す必要はありません。自動的に処理されます。

警告

セキュリティ警告: 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 プロパティだけです。 スタック トレースと例外のその他のプロパティは、クライアントでは使用できません。

その他のリソース