ASP.NET Core SignalR でハブ フィルターを使う
ハブ フィルターの特長:
- ASP.NET Core 5.0 以降で使用できます。
- ハブ メソッドがクライアントによって呼び出される前後でロジックを実行できます。
この記事では、ハブ フィルターの作成と使用に関するガイダンスを提供します。
ハブ フィルターを構成する
ハブ フィルターは、グローバルに、またはハブの種類ごとに適用できます。 フィルターを追加する順序は、フィルターが実行される順序になります。 グローバル ハブ フィルターは、ローカルのハブ フィルターの前に実行されます。
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR(options =>
{
// Global filters will run first
options.AddFilter<CustomFilter>();
}).AddHubOptions<ChatHub>(options =>
{
// Local filters will run second
options.AddFilter<CustomFilter2>();
});
}
ハブ フィルターは、次のいずれかの方法で追加できます。
具象型によるフィルターの追加:
hubOptions.AddFilter<TFilter>();
これは、依存関係の挿入 (DI) またはアクティブ化された型から解決されます。
ランタイム型によるフィルターの追加:
hubOptions.AddFilter(typeof(TFilter));
これは、DI またはアクティブ化された型から解決されます。
インスタンスによるフィルターの追加:
hubOptions.AddFilter(new MyFilter());
このインスタンスは、シングルトンのように使われます。 すべてのハブ メソッドの呼び出しで同じインスタンスが使われます。
ハブ フィルターは、ハブの呼び出しごとに作成および破棄されます。 グローバル状態をフィルターに格納する場合、または状態を指定しない場合は、パフォーマンスを向上させるために、ハブ フィルターの種類をシングルトンとして DI に追加します。 または、可能な場合は、フィルターをインスタンスとして追加します。
ハブ フィルターを作成する
IHubFilter
から継承するクラスを宣言してフィルターを作成し、InvokeMethodAsync
メソッドを追加します。 また、必要に応じて OnConnectedAsync
と OnDisconnectedAsync
を実装し、それぞれ OnConnectedAsync
と OnDisconnectedAsync
のハブ メソッドをラップすることもできます。
public class CustomFilter : IHubFilter
{
public async ValueTask<object> InvokeMethodAsync(
HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next)
{
Console.WriteLine($"Calling hub method '{invocationContext.HubMethodName}'");
try
{
return await next(invocationContext);
}
catch (Exception ex)
{
Console.WriteLine($"Exception calling '{invocationContext.HubMethodName}': {ex}");
throw;
}
}
// Optional method
public Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
{
return next(context);
}
// Optional method
public Task OnDisconnectedAsync(
HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
{
return next(context, exception);
}
}
フィルターはミドルウェアとよく似ています。 next
メソッドは、次のフィルターを呼び出します。 最後のフィルターは、ハブ メソッドを呼び出します。 また、フィルターでは、待機している next
からの結果を格納したり、ハブ メソッドが呼ばれた後 next
から結果が返される前にロジックを実行したりできます。
フィルターでハブ メソッドの呼び出しをスキップするには、next
を呼び出す代わりに、型 HubException
の例外をスローします。 結果が求められていた場合、クライアントはエラーを受け取ります。
ハブ フィルターを使う
フィルター ロジックを記述するときは、ハブ メソッド名をチェックするのではなく、ハブ メソッドの属性を利用して汎用性を持たせるようにします。
ハブ メソッドの引数に禁止語句がないかチェックして、見つかった語句を ***
に置き換えるフィルターについて考えてみます。
この例では、LanguageFilterAttribute
クラスが定義されているとします。 クラスには、属性を利用するときに設定できる FilterArgument
という名前のプロパティがあります。
この属性を、クリーニング対象の文字列引数を持つハブ メソッドに設定します。
public class ChatHub { [LanguageFilter(filterArgument = 0)] public async Task SendMessage(string message, string username) { await Clients.All.SendAsync("SendMessage", $"{username} says: {message}"); } }
ハブ フィルターを定義して属性をチェックし、ハブ メソッドの引数の禁止語句を
***
に置き換えます。public class LanguageFilter : IHubFilter { // populated from a file or inline private List<string> bannedPhrases = new List<string> { "async void", ".Result" }; public async ValueTask<object> InvokeMethodAsync(HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next) { var languageFilter = (LanguageFilterAttribute)Attribute.GetCustomAttribute( invocationContext.HubMethod, typeof(LanguageFilterAttribute)); if (languageFilter != null && invocationContext.HubMethodArguments.Count > languageFilter.FilterArgument && invocationContext.HubMethodArguments[languageFilter.FilterArgument] is string str) { foreach (var bannedPhrase in bannedPhrases) { str = str.Replace(bannedPhrase, "***"); } var arguments = invocationContext.HubMethodArguments.ToArray(); arguments[languageFilter.FilterArgument] = str; invocationContext = new HubInvocationContext(invocationContext.Context, invocationContext.ServiceProvider, invocationContext.Hub, invocationContext.HubMethod, arguments); } return await next(invocationContext); } }
Startup.ConfigureServices
メソッドでハブ フィルターを登録します。 すべての呼び出しでの禁止語句リストの再初期化を避けるために、ハブ フィルターはシングルトンとして登録されます。public void ConfigureServices(IServiceCollection services) { services.AddSignalR(hubOptions => { hubOptions.AddFilter<LanguageFilter>(); }); services.AddSingleton<LanguageFilter>(); }
HubInvocationContext オブジェクト
HubInvocationContext
には、現在のハブ メソッド呼び出しに関する情報が含まれます。
プロパティ | 説明 | Type |
---|---|---|
Context |
HubCallerContext には接続に関する情報が格納されます。 |
HubCallerContext |
Hub |
このハブ メソッドの呼び出しに使われているハブのインスタンス。 | Hub |
HubMethodName |
呼び出されているハブ メソッドの名前。 | string |
HubMethodArguments |
ハブ メソッドに渡される引数のリスト。 | IReadOnlyList<string> |
ServiceProvider |
このハブ メソッド呼び出しのスコープを持つサービス プロバイダー。 | IServiceProvider |
HubMethod |
ハブ メソッドの情報。 | MethodInfo |
HubLifetimeContext オブジェクト
HubLifetimeContext
には、OnConnectedAsync
と OnDisconnectedAsync
ハブ メソッドの情報が格納されています。
プロパティ | 説明 | Type |
---|---|---|
Context |
HubCallerContext には接続に関する情報が格納されます。 |
HubCallerContext |
Hub |
このハブ メソッドの呼び出しに使われているハブのインスタンス。 | Hub |
ServiceProvider |
このハブ メソッド呼び出しのスコープを持つサービス プロバイダー。 | IServiceProvider |
認可とフィルター
ハブ フィルターの前に実行されるハブ メソッドの認可属性。
ASP.NET Core
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示