作成者: Kirk Larkin、Rick Anderson、Tom Dykstra、Steve Smith
ASP.NET Core でフィルターを使用すると、要求処理パイプラインの特定のステージの前または後にコードを実行できます。
組み込みのフィルターでは次のようなタスクが処理されます。
- 許可。ユーザーに許可が与えられていないリソースの場合、アクセスを禁止する。
- 応答キャッシュ。要求パイプラインを迂回し、キャッシュされている応答を返す。
横断的な問題を処理するカスタム フィルターを作成できます。 横断的な問題の例には、エラー処理、キャッシュ、構成、認証、ログなどがあります。 フィルターにより、コードの重複が回避されます。 たとえば、エラー処理例外フィルターではエラー処理を統合できます。
このドキュメントは、Razor Pages、API コントローラー、ビューのあるコントローラーに当てはまります。 フィルターは、Razor コンポーネントでは直接機能しません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。
- コンポーネントがページまたはビューに埋め込まれている。
- ページまたはコントローラーとビューでフィルターが使用されている。
フィルターのしくみ
ASP.NET Core のアクション呼び出しパイプライン内で実行されるフィルターは、フィルター パイプラインと呼ばれることがあります。 フィルター パイプラインは、ASP.NET Core が実行するアクションを選択した後に実行します。
フィルター タイプ
フィルターの種類はそれぞれ、フィルター パイプラインの異なるステージで実行されます。
-
- 最初に実行します。
- ユーザーが要求に対して承認されているかどうかを判断します。
- リクエストが未承認の場合、パイプラインを中断します。
-
- 承認後に実行されます。
-
OnResourceExecuting は、フィルター パイプラインの残りの部分の前にコードを実行します。 たとえば、
OnResourceExecutingでは、モデル バインディングの前にコードが実行されます。 - OnResourceExecuted、パイプラインの残りの部分が完了した後にコードを実行します。
-
- アクション メソッドが呼び出される直前と直後に実行します。
- アクションに渡される引数を変更できます。
- アクションから返された結果を変更できます。
- Pages ではサポートされてRazor。
-
- アクション メソッドが呼び出される直前と直後に実行します。
- アクションに渡される引数を変更できます。
- アクションから返された結果を変更できます。
- Pages ではサポートされてRazor。
- ルートハンドラー ベースのエンドポイントとコントローラー アクションの両方で呼び出すことができます。
-
- 応答本文が書き込まれる前に発生するハンドルされない例外にグローバル ポリシーを適用します。
- モデル バインドとアクション フィルターの後、アクションの結果が実行される前に実行されます。
- アクションの実行中またはアクションの結果の実行中にハンドルされない例外が発生した場合にのみ実行します。
- ミドルウェアの実行中、ルーティング中、またはモデル バインド中にスローされた例外に対しては、実行しません。
-
- アクション結果の実行の直前と直後に実行します。
- アクション メソッドが正常に実行された場合にのみ実行します。
- ビューまたはフォーマッタ実行を取り囲む必要があるロジックに便利です。
Razor ページは、ページ ハンドラーの前後に実行されるRazor ページ フィルターもサポートします。
Implementation
フィルターは、異なるインターフェイス定義を介して、同期と非同期の実装をサポートします。
同期フィルターは、そのパイプライン ステージの前後に実行します。 たとえば、OnActionExecuting はアクション メソッドの呼び出し前に呼び出されます。 OnActionExecuted は、アクション メソッドが戻った後に呼び出されます。
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
}
}
非同期フィルターでは、On-Stage-ExecutionAsync メソッドが定義されます。
OnActionExecutionAsync の例を次に示します。
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
// Do something before the action executes.
await next();
// Do something after the action executes.
}
}
上記のコードでは、SampleAsyncActionFilter の ActionExecutionDelegate には、アクションメソッドを実行する next があります。
複数のフィルター ステージ
複数のフィルター ステージのためのインターフェイスを 1 つのクラスで実装できます。 たとえば、ActionFilterAttribute クラスでは次のものが実装されます。
フィルター インターフェイスの同期と非同期バージョンの両方ではなく、いずれかを実装します。 ランタイムは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。 していない場合は、同期インターフェイスのメソッドを呼び出します。 非同期インターフェイスと同期インターフェイスの両方が 1 つのクラスで実装される場合、非同期メソッドのみが呼び出されます。 ActionFilterAttribute などの抽象クラスを使用する場合は、同期メソッドのみをオーバーライドするか、フィルターの種類ごとに非同期メソッドをオーバーライドします。
組み込みのフィルター属性
ASP.NET Core には、サブクラスを作成したり、カスタマイズしたりできる組み込みの属性ベースのフィルターが含まれます。 たとえば、次の結果フィルターは、応答にヘッダーを追加します。
public class ResponseHeaderAttribute : ActionFilterAttribute
{
private readonly string _name;
private readonly string _value;
public ResponseHeaderAttribute(string name, string value) =>
(_name, _value) = (name, value);
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(_name, _value);
base.OnResultExecuting(context);
}
}
上記の例のように、属性によってフィルターは引数を受け取ることができます。
ResponseHeaderAttribute をコントローラーまたはアクション メソッドに適用し、HTTP ヘッダーの名前と値を指定します。
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
ブラウザー開発者ツール などのツールを使用して、ヘッダーを調べます。
[応答ヘッダー] の下に filter-header: Filter Value が表示されます。
次のコードでは、コントローラーとアクションの両方に ResponseHeaderAttribute が適用されます。
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
[ResponseHeader("Another-Filter-Header", "Another Filter Value")]
public IActionResult Multiple() =>
Content("Examine the response headers using the F12 developer tools.");
}
Multiple アクションからの応答には、次のヘッダーが含まれます。
filter-header: Filter Valueanother-filter-header: Another Filter Value
フィルター インターフェイスのいくつかには対応する属性があり、カスタムの実装に基底クラスとして使用できます。
フィルター属性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Razor ページ ハンドラー メソッドにフィルターを適用することはできません。 これらは、Razor ページ モデルに適用するか、グローバルに適用することが可能です。
フィルターのスコープと実行の順序
フィルターは、3 つのスコープのいずれかでパイプラインに追加することができます。
- コントローラーまたは Razor ページでの属性の使用。
- コントローラー アクションでの属性の使用。 フィルター属性を Razor Pages ハンドラー メソッドに適用することはできません。
- 次のコードのように、すべてのコントローラー、アクション、および Razor Pages に対してグローバルに:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
実行の既定の順序
パイプラインの特定のステージに対して複数のフィルターがある場合に、スコープがフィルターの実行の既定の順序を決定します。 グローバル フィルターがクラス フィルターを囲み、クラス フィルターがメソッド フィルターを囲みます。
フィルターの入れ子の結果として、フィルターの after コードが before コードと逆の順序で実行されます。 フィルター シーケンス:
- グローバル フィルターの before コード。
- コントローラー フィルターの before コード。
- アクション メソッド フィルターの before コード。
- アクション メソッド フィルターの after コード。
- コントローラー フィルターの after コード。
- コントローラー フィルターの before コード。
- グローバル フィルターの after コード。
次の例は、同期アクション フィルターに対してフィルター メソッドが実行される順序を示しています。
| Sequence | フィルター スコープ | Filter メソッド |
|---|---|---|
| 1 | Global | OnActionExecuting |
| 2 | Controller | OnActionExecuting |
| 3 | Action | OnActionExecuting |
| 4 | Action | OnActionExecuted |
| 5 | Controller | OnActionExecuted |
| 6 | Global | OnActionExecuted |
コントローラー レベルのフィルター
Controller から継承するすべてのコントローラーには、OnActionExecuting、OnActionExecutionAsync、OnActionExecuted メソッドが含まれます。 これらのメソッドは、特定のアクションに対して実行されるフィルターをラップします。
-
OnActionExecutingは、すべてのアクション フィルターの前に実行します。 -
OnActionExecutedは、すべてのアクション フィルターの後に実行します。 -
OnActionExecutionAsyncは、すべてのアクション フィルターの前に実行します。 アクションのフィルターの後に、nextへの呼び出し後のコードを実行します。
次の ControllerFiltersController クラスは、次のように動作します。
-
SampleActionFilterAttribute([SampleActionFilter]) をコントローラーに適用します。 -
OnActionExecutingとOnActionExecutedがオーバーライドされます。
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");
base.OnActionExecuted(context);
}
public IActionResult Index()
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(Index)}");
return Content("Check the Console.");
}
}
https://localhost:<port>/ControllerFilters に移動すると、次のコードが実行されます。
ControllerFiltersController.OnActionExecutingGlobalSampleActionFilter.OnActionExecutingSampleActionFilterAttribute.OnActionExecutingControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することはできません。 順序については、次のセクションで説明します。
Razor ページについては、フィルターメソッドをオーバーライドして Razor ページフィルターを実装するを参照してください。
既定の順序をオーバーライドする
IOrderedFilter を実装することで、実行の既定の順序をオーバーライドできます。
IOrderedFilter により、実行の順序を決定するために、スコープよりも優先される Order プロパティが公開されます。 より低い Order 値を持つフィルター:
- より高い の値を持つフィルターのそれよりも前に
Orderコードが実行されます。 - より高い の値を持つフィルターのそれよりも後に
Orderコードが実行されます。
コントローラー レベルのフィルターの例では、GlobalSampleActionFilter はグローバル スコープを持つため、コントローラー スコープを持つ SampleActionFilterAttribute の前に実行します。
SampleActionFilterAttribute を最初に実行するようにするには、次のように順序を int.MinValue に設定します。
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
グローバル フィルター GlobalSampleActionFilter を最初に実行するようにするには、次のように Order を int.MinValue に設定します。
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
キャンセルとショートサーキット
フィルター メソッドに提供される Result パラメーターで ResourceExecutingContext プロパティを設定することで、フィルター パイプラインをショートサーキットできます。 たとえば、次のリソース フィルターを使用すると、パイプラインの残りの部分が実行されなくなります。
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
次のコードでは、[ShortCircuitingResourceFilter] と [ResponseHeader] の両方のフィルターが Index アクション メソッドをターゲットにしています。
ShortCircuitingResourceFilterAttribute フィルターは、次のようになります。
- これはリソース フィルターであり、
ResponseHeaderAttributeはアクション フィルターであるため、最初に実行されます。 - パイプラインの残りの部分は迂回されます。
そのため、ResponseHeaderAttribute アクションの場合、Index フィルターが実行されることはありません。
ShortCircuitingResourceFilterAttribute が最初に実行された場合は、両方のフィルターがアクション メソッド レベルで適用されると、この動作が同じになります。
ShortCircuitingResourceFilterAttribute は、フィルターの種類のために最初に実行します。
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
依存関係の挿入
フィルターは種類ごとまたはインスタンスごとに追加できます。 インスタンスが追加される場合、そのインスタンスはすべての要求に対して使用されます。 種類が追加される場合、それは種類でアクティブ化されます。 種類によって稼働するフィルターの意味は次のとおりです。
- インスタンスは要求ごとに作成されます。
- コンストラクターの依存関係は、依存関係の挿入 (DI) によって入力されます。
属性として実装され、コントローラー クラスまたはアクション メソッドに直接追加されるフィルターは、依存関係の挿入 (DI) によって提供されるコンストラクターの依存関係を持つことはできません。 属性には、適用される場所で提供される独自のコンストラクター パラメーターが必要であるため、コンストラクターの依存関係は、DI によって与えられません。
次のフィルターでは、DI から提供されるコンストラクターの依存関係がサポートされます。
上記のフィルターは、コントローラーまたはアクションに適用できます。
ロガーは DI から利用できます。 ただし、ログ目的でのみフィルターを作成し、使用することは避けてください。 組み込みフレームワークのログ機能で通常、ログ記録に必要なものが与えられます。 フィルターに追加されるログ記録:
- ビジネス ドメインの懸念事項やフィルターに固有の動作に焦点を合わせます。
- アクションやその他のフレームワーク イベントはログに記録しないでください。 組み込みのフィルターにより、アクションとフレームワーク イベントが既に記録されています。
ServiceFilterAttribute
サービス フィルターの実装の種類は Program.cs に登録されています。
ServiceFilterAttribute は DI からフィルターのインスタンスを取得します。
次のコードは、DI を使用する LoggingResponseHeaderFilterService クラスを示しています。
public class LoggingResponseHeaderFilterService : IResultFilter
{
private readonly ILogger _logger;
public LoggingResponseHeaderFilterService(
ILogger<LoggingResponseHeaderFilterService> logger) =>
_logger = logger;
public void OnResultExecuting(ResultExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
context.HttpContext.Response.Headers.Add(
nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
}
public void OnResultExecuted(ResultExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
}
}
次のコードでは、LoggingResponseHeaderFilterService が DI コンテナーに追加されます。
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
次のコードでは、ServiceFilter 属性により、DI から LoggingResponseHeaderFilterService フィルターのインスタンスが取得されます。
[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
ServiceFilterAttribute を使用する場合、ServiceFilterAttribute.IsReusable を設定します。
- フィルター インスタンスが、それが作成された要求範囲の外で再利用される可能性があることを示唆します。 ASP.NET Core ランタイムによって保証されない事項は次のとおりです。
- フィルターのインスタンスが 1 つ作成されます。
- フィルターが後の時点で、DI コンテナーから再要求されることはありません。
- シングルトン以外で有効期間があるサービスに依存するフィルターと共にを使用しないでください。
ServiceFilterAttribute は、IFilterFactory を実装します。
IFilterFactory は、CreateInstance インスタンスを作成するために IFilterMetadata メソッドを公開します。
CreateInstance により、DI から指定の型が読み込まれます。
TypeFilterAttribute
TypeFilterAttribute はServiceFilterAttribute と似ていますが、その型は DI コンテナーから直接解決されません。 Microsoft.Extensions.DependencyInjection.ObjectFactory を使って型をインスタンス化します。
TypeFilterAttribute 型は DI コンテナーから直接解決されないためです。
-
TypeFilterAttributeを利用して参照される型は、DI コンテナーに登録する必要がありません。 DI コンテナーによって依存関係が満たされています。 -
TypeFilterAttributeは必要に応じて、型のコンストラクター引数を受け取ることができます。
TypeFilterAttribute を使用する場合、TypeFilterAttribute.IsReusable を設定します。
フィルター インスタンスが、それが作成された要求範囲の外で再利用される可能性があることを示唆します。 ASP.NET Core ランタイムでは、フィルターの 1 インスタンスが作成されるという保証はありません。
シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
次の例は、TypeFilterAttribute を使用して、型に引数を渡す方法を示しています。
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
承認フィルター
承認フィルター:
- フィルター パイプライン内で実行される最初のフィルターです。
- アクション メソッドへのアクセスを制御します。
- before メソッドが与えられ、after メソッドは与えられません。
カスタム承認フィルターには、カスタム承認フレームワークが必要です。 カスタム フィルターを記述するよりも、独自の承認ポリシーを構成するか、カスタム承認ポリシーを記述することを選びます。 組み込み承認フィルター:
- 承認システムを呼び出します。
- 要求を承認しません。
承認フィルター内で例外をスローしません。
- 例外は処理されません。
- 例外フィルターで例外が処理されません。
承認フィルターで例外が発生した場合、チャレンジ発行を検討してください。
承認の詳細については、こちらを参照してください。
リソース フィルター
リソース フィルター:
- IResourceFilter または IAsyncResourceFilter のインターフェイスを実装します。
- 実行により、ほとんどのフィルター パイプラインがラップされます。
- 承認フィルターのみ、リソース フィルターの前に実行されます。
リソースフィルターはほとんどのパイプラインをショートカットするのに役立ちます。 たとえば、キャッシュ フィルターを使用すると、キャッシュ ヒット時のパイプラインの残りの部分を回避できます。
リソース フィルターの例:
DisableFormValueModelBindingAttribute:
- モデル バインドがフォーム データにアクセスすることを禁止します。
- メモリにフォーム データが読み込まれないようにする目的で大きなファイルのアップロードに使用されます。
アクション フィルター
アクションフィルターはPagesには適用されません。 Razor Pages では IPageFilter と IAsyncPageFilter がサポートされています。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。
アクション フィルター:
- IActionFilter または IAsyncActionFilter のインターフェイスを実装します。
- この実行はアクション メソッドの実行を取り囲みます。
次のコードは、サンプル アクション フィルターを示しています。
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
}
}
ActionExecutingContext では次のプロパティが提供されます。
- ActionArguments - アクション メソッドへの入力の読み取りを有効にします。
- Controller - コントローラー インスタンスを操作できます。
-
Result -
Resultを設定すると、アクション メソッドとそれに続くアクション フィルターの実行が中断されます。
アクション メソッドで例外をスローする:
- 後続のフィルターの実行を回避します。
-
Resultの設定とは異なり、結果は成功ではなく、失敗として処理されます。
ActionExecutedContext は、Controller と Result に加え、次のプロパティを提供します。
- Canceled - 別のフィルターによってアクションの実行が中断された場合は、true です。
-
Exception - アクションや以前に実行されたアクション フィルターが例外をスローした場合、null 以外の値になります。 このプロパティを null に設定する:
- 例外が効果的に処理されます。
-
Resultは、アクション メソッドから返されたかのように実行されます。
IAsyncActionFilter の場合、ActionExecutionDelegate の呼び出しによって:
- 後続のすべてのアクション フィルターとアクション メソッドが実行されます。
-
ActionExecutedContextを返します。
ショートサーキットさせるには、Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result を結果インスタンスに割り当て、next(あるいは ActionExecutionDelegate)を呼び出してはいけません。
このフレームワークからは、サブクラス化できる抽象 ActionFilterAttribute が与えられます。
OnActionExecuting アクション フィルターを次の目的で使用できます。
- モデルの状態を検証します。
- 状態が有効でない場合は、エラーを返します。
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Note
[ApiController] 属性で注釈が付けられたコントローラーでは、モデルの状態を自動的に検証し、400 の応答を返します。 詳細については、「自動的な HTTP 400 応答」を参照してください。
OnActionExecuted メソッドは、アクション メソッドの後に実行されます。
- また、Result プロパティを介してアクションの結果を表示したり、操作したりできます。
- Canceled は、アクションの実行が別のフィルターによって短絡された時、true に設定されます。
- アクションまたは後続のアクション フィルターが例外をスローした場合、Exception は null 以外の値に設定されます。
Exceptionを null に設定すると:- 例外が効果的に処理されます。
-
ActionExecutedContext.Resultは、アクション メソッドから通常どおり返されたかのように実行されます。
例外フィルター
例外フィルター:
- IExceptionFilter または IAsyncExceptionFilter を実装します。
- 共通のエラー処理ポリシーを実装する目的で使用できます。
次の例外フィルターのサンプルでは、アプリの開発中に発生する例外に関する詳細を表示します。
public class SampleExceptionFilter : IExceptionFilter
{
private readonly IHostEnvironment _hostEnvironment;
public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
_hostEnvironment = hostEnvironment;
public void OnException(ExceptionContext context)
{
if (!_hostEnvironment.IsDevelopment())
{
// Don't display exception details unless running in Development.
return;
}
context.Result = new ContentResult
{
Content = context.Exception.ToString()
};
}
}
次のコードでは、例外フィルターをテストします。
[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
例外フィルター:
- 前後のイベントがありません。
- OnException または OnExceptionAsync を実装します。
- Razor Page またはコントローラーの作成、モデル バインド、アクション フィルター、またはアクション メソッドで発生する未処理の例外を処理します。
- リソース フィルター、結果フィルター、または MVC 結果の実行で発生した例外はキャッチしません。
例外を処理するには、ExceptionHandled プロパティを true に設定するか、Result プロパティを割り当てます。 これにより、例外の伝達を停止します。 例外フィルターで例外を "成功" に変えることはできません。 それができるのは、アクション フィルターだけです。
例外フィルター:
- アクション内で発生する例外のトラップに適しています。
- エラー処理ミドルウェアほど柔軟ではありません。
例外処理にはミドルウェアを選択してください。 呼び出されたアクション メソッドに基づいてエラー処理が異なる状況でのみ例外フィルターを使用します。 たとえば、アプリには、API エンドポイントとビュー/HTML の両方に対するアクション メソッドがある場合があります。 API エンドポイントは、JSON としてのエラー情報を返す可能性がある一方で、ビュー ベースのアクションがエラー ページを HTML として返す可能性があります。
結果フィルター
結果フィルター:
- インターフェイスを実装します:
- この実行はアクション結果の実行を取り囲みます。
IResultFilter および IAsyncResultFilter
次のコードは、結果フィルターの例を示しています。
public class SampleResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
// Do something before the result executes.
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Do something after the result executes.
}
}
実行されている結果の種類は、アクションに依存します。 ビューを返すアクションには、実行されている ViewResult の一部として、すべての razor 処理が含まれます。 API メソッドは、結果の実行の一部としていくつかのシリアル化を実行できます。 アクション結果に関する詳細を参照してください。
結果フィルターは、アクションまたはアクション フィルターによってアクション結果を生成される場合にのみ実行されます。 結果フィルターは、次の場合には実行されません。
- 承認フィルターまたはリソース フィルターによって、パイプラインがショートサーキットされる。
- アクションの結果を生成することで、例外フィルターによって例外が処理されます。
Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting メソッドは、Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel を true に設定することで、アクションの結果と後続の結果フィルターの実行をショートサーキットできます。 ショートサーキットする場合は、空の応答が生成されないように応答オブジェクトに記述します。
IResultFilter.OnResultExecuting での例外のスロー:
- アクション結果と後続フィルターの実行を回避します。
- 結果は成功ではなく、失敗として処理されます。
Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted メソッドが実行されたとき、おそらく応答は既にクライアントに送信されています。 応答が既にクライアントに送信されていた場合は、それを変更することはできません。
別のフィルターによってアクション結果の実行がショートサーキットされた場合、ResultExecutedContext.Canceled は true に設定されます。
アクションの結果または後続の結果フィルターが例外をスローした場合、ResultExecutedContext.Exception は null 以外の値に設定されます。
Exception を null に設定すると、例外を効果的に処理し、パイプラインの後方で例外が再スローされるのを防ぐことができます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。
IAsyncResultFilter の場合、await next で ResultExecutionDelegate を呼び出すと、後続のすべての結果フィルターとアクションの結果が実行されます。 ショートサーキットするには、ResultExecutingContext.Cancel を true に設定します。ResultExecutionDelegate は呼び出さないでください。
public class SampleAsyncResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is not EmptyResult)
{
await next();
}
else
{
context.Cancel = true;
}
}
}
このフレームワークからは、サブクラス化できる抽象 ResultFilterAttribute が与えられます。 前に示した ResponseHeaderAttribute クラスは、結果フィルター属性の一例です。
IAlwaysRunResultFilter および IAsyncAlwaysRunResultFilter
IAlwaysRunResultFilter および IAsyncAlwaysRunResultFilter インターフェイスでは、すべてのアクションの結果に対して実行される IResultFilter の実装が宣言されます。 これには、以下によって生成されるアクションの結果が含まれます。
- ショートサーキットが行われる承認フィルターとリソース フィルター。
- 例外フィルター。
たとえば、次のフィルターは常に実行され、コンテンツ ネゴシエーションが失敗した場合に ObjectResult 状態コードを使ってアクションの結果 () を設定します。
public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult
&& statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
{
context.Result = new ObjectResult("Unprocessable")
{
StatusCode = StatusCodes.Status422UnprocessableEntity
};
}
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
IFilterFactory
IFilterFactory は、IFilterMetadata を実装します。 そのため、IFilterFactory インスタンスはフィルター パイプライン内の任意の場所で IFilterMetadata インスタンスとして使用できます。 ランタイムでは、フィルターを呼び出す準備をする際、IFilterFactory へのキャストが試行されます。 そのキャストが成功した場合、呼び出される CreateInstance インスタンスを作成するために IFilterMetadata メソッドが呼び出されます。 これにより、アプリの起動時に正確なフィルター パイプラインを明示的に設定する必要がないため、柔軟なデザインが可能になります。
IFilterFactory.IsReusable:
- ファクトリによって作成されたフィルター インスタンスは、それが作成された要求範囲の外で使用される可能性があることがファクトリによって示されています。
- シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
ASP.NET Core ランタイムで保証されないこと:
- フィルターのインスタンスが 1 つ作成されます。
- フィルターが後の時点で、DI コンテナーから再要求されることはありません。
Warning
フィルターのソースが明確であり、フィルターがステートレスであり、かつ複数の HTTP リクエストで安全に使用できる場合にのみ、IFilterFactory.IsReusable を true で返すように設定します。 たとえば、IFilterFactory.IsReusable で true が返される場合、範囲付きまたは一時的として登録されているフィルターを DI から返さないようにします。
フィルターを作成するための別の方法として、カスタムの属性の実装で IFilterFactory を実装できます。
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
フィルターは次のコードで適用されます。
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
属性上に実装された IFilterFactory
IFilterFactory を実装するフィルターは次のようなフィルターに便利です。
- パラメーターの渡しを要求しない。
- DI で満たす必要があるコンストラクター依存関係があります。
TypeFilterAttribute は、IFilterFactory を実装します。
IFilterFactory は、CreateInstance インスタンスを作成するために IFilterMetadata メソッドを公開します。
CreateInstance により、サービス コンテナー (DI) から指定の型が読み込まれます。
public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
public SampleActionTypeFilterAttribute()
: base(typeof(InternalSampleActionFilter)) { }
private class InternalSampleActionFilter : IActionFilter
{
private readonly ILogger<InternalSampleActionFilter> _logger;
public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
_logger = logger;
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
}
}
}
次のコードでは、フィルターを適用する 3 つの手法を確認できます。
[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
[TypeFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithTypeFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
[ServiceFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithServiceFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");
前のコードでは、フィルターを適用するための最初の方法をお勧めします。
フィルター パイプラインでミドルウェアを使用する
リソースフィルターは、パイプライン内の後に続くすべての処理を囲むように、ミドルウェアのように機能します。 ただし、フィルターはランタイムの一部である点がミドルウェアとは異なります。つまり、それらはコンテキストとコンストラクトにアクセスすることができます。
ミドルウェアをフィルターとして使用するには、フィルター パイプラインに挿入するミドルウェアを指定する Configure メソッドを使用して型を作成します。 次の例では、ミドルウェアを使用して応答ヘッダーを設定します。
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
MiddlewareFilterAttribute を使用し、ミドルウェアを実行します。
[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
ミドルウェア フィルターは、モデル バインドの前とパイプラインの残りの部分の後で、リソース フィルターと同じ段階でフィルター パイプラインで実行されます。
スレッドの安全性
その の代わりにフィルターの "Add" を Type に渡すとき、フィルターはシングルトンであり、スレッドセーフではありません。
その他のリソース
作成者: Kirk Larkin、Rick Anderson、Tom Dykstra、Steve Smith
ASP.NET Core でフィルターを使用すると、要求処理パイプラインの特定のステージの前または後にコードを実行できます。
組み込みのフィルターでは次のようなタスクが処理されます。
- 許可。ユーザーに許可が与えられていないリソースの場合、アクセスを禁止する。
- 応答キャッシュ。要求パイプラインを迂回し、キャッシュされている応答を返す。
横断的な問題を処理するカスタム フィルターを作成できます。 横断的な問題の例には、エラー処理、キャッシュ、構成、認証、ログなどがあります。 フィルターにより、コードの重複が回避されます。 たとえば、エラー処理例外フィルターではエラー処理を統合できます。
このドキュメントは、Razor Pages、API コントローラー、ビューのあるコントローラーに当てはまります。 フィルターは、Razor コンポーネントでは直接機能しません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。
- コンポーネントがページまたはビューに埋め込まれている。
- ページまたはコントローラーとビューでフィルターが使用されている。
フィルターのしくみ
ASP.NET Core のアクション呼び出しパイプライン内で実行されるフィルターは、フィルター パイプラインと呼ばれることがあります。 フィルター パイプラインは、ASP.NET Core が実行するアクションを選択した後に実行します。
フィルター タイプ
フィルターの種類はそれぞれ、フィルター パイプラインの異なるステージで実行されます。
-
- 最初に実行します。
- ユーザーが要求に対して承認されているかどうかを判断します。
- リクエストが未承認の場合、パイプラインを中断します。
-
- 承認後に実行されます。
-
OnResourceExecuting は、フィルター パイプラインの残りの部分の前にコードを実行します。 たとえば、
OnResourceExecutingでは、モデル バインディングの前にコードが実行されます。 - OnResourceExecuted、パイプラインの残りの部分が完了した後にコードを実行します。
-
- アクション メソッドが呼び出される直前と直後に実行します。
- アクションに渡される引数を変更できます。
- アクションから返された結果を変更できます。
- Pages ではサポートされてRazor。
-
- アクション メソッドが呼び出される直前と直後に実行します。
- アクションに渡される引数を変更できます。
- アクションから返された結果を変更できます。
- Pages ではサポートされてRazor。
- ルートハンドラー ベースのエンドポイントとコントローラー アクションの両方で呼び出すことができます。
例外フィルターでは、応答本文への書き込みが行われる前に発生する未処理の例外にグローバル ポリシーが適用されます。
-
- アクション結果の実行の直前と直後に実行します。
- アクション メソッドが正常に実行された場合にのみ実行します。
- ビューまたはフォーマッタ実行を取り囲む必要があるロジックに便利です。
フィルターの種類がフィルター パイプラインでどのように連携しているかを、次の図に示します。
Razor ページは、ページ ハンドラーの前後に実行されるRazor ページ フィルターもサポートします。
Implementation
フィルターは、異なるインターフェイス定義を介して、同期と非同期の実装をサポートします。
同期フィルターは、そのパイプライン ステージの前後に実行します。 たとえば、OnActionExecuting はアクション メソッドの呼び出し前に呼び出されます。 OnActionExecuted は、アクション メソッドが戻った後に呼び出されます。
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
}
}
非同期フィルターでは、On-Stage-ExecutionAsync メソッドが定義されます。
OnActionExecutionAsync の例を次に示します。
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
// Do something before the action executes.
await next();
// Do something after the action executes.
}
}
上記のコードでは、SampleAsyncActionFilter の ActionExecutionDelegate には、アクションメソッドを実行する next があります。
複数のフィルター ステージ
複数のフィルター ステージのためのインターフェイスを 1 つのクラスで実装できます。 たとえば、ActionFilterAttribute クラスでは次のものが実装されます。
フィルター インターフェイスの同期と非同期バージョンの両方ではなく、いずれかを実装します。 ランタイムは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。 していない場合は、同期インターフェイスのメソッドを呼び出します。 非同期インターフェイスと同期インターフェイスの両方が 1 つのクラスで実装される場合、非同期メソッドのみが呼び出されます。 ActionFilterAttribute などの抽象クラスを使用する場合は、同期メソッドのみをオーバーライドするか、フィルターの種類ごとに非同期メソッドをオーバーライドします。
組み込みのフィルター属性
ASP.NET Core には、サブクラスを作成したり、カスタマイズしたりできる組み込みの属性ベースのフィルターが含まれます。 たとえば、次の結果フィルターは、応答にヘッダーを追加します。
public class ResponseHeaderAttribute : ActionFilterAttribute
{
private readonly string _name;
private readonly string _value;
public ResponseHeaderAttribute(string name, string value) =>
(_name, _value) = (name, value);
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(_name, _value);
base.OnResultExecuting(context);
}
}
上記の例のように、属性によってフィルターは引数を受け取ることができます。
ResponseHeaderAttribute をコントローラーまたはアクション メソッドに適用し、HTTP ヘッダーの名前と値を指定します。
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
ブラウザー開発者ツール などのツールを使用して、ヘッダーを調べます。
[応答ヘッダー] の下に filter-header: Filter Value が表示されます。
次のコードでは、コントローラーとアクションの両方に ResponseHeaderAttribute が適用されます。
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
[ResponseHeader("Another-Filter-Header", "Another Filter Value")]
public IActionResult Multiple() =>
Content("Examine the response headers using the F12 developer tools.");
}
Multiple アクションからの応答には、次のヘッダーが含まれます。
filter-header: Filter Valueanother-filter-header: Another Filter Value
フィルター インターフェイスのいくつかには対応する属性があり、カスタムの実装に基底クラスとして使用できます。
フィルター属性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Razor ページ ハンドラー メソッドにフィルターを適用することはできません。 これらは、Razor ページ モデルに適用するか、グローバルに適用することが可能です。
フィルターのスコープと実行の順序
フィルターは、3 つのスコープのいずれかでパイプラインに追加することができます。
- コントローラーまたは Razor ページでの属性の使用。
- コントローラー アクションでの属性の使用。 フィルター属性を Razor Pages ハンドラー メソッドに適用することはできません。
- 次のコードのように、すべてのコントローラー、アクション、および Razor Pages に対してグローバルに:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
実行の既定の順序
パイプラインの特定のステージに対して複数のフィルターがある場合に、スコープがフィルターの実行の既定の順序を決定します。 グローバル フィルターがクラス フィルターを囲み、クラス フィルターがメソッド フィルターを囲みます。
フィルターの入れ子の結果として、フィルターの after コードが before コードと逆の順序で実行されます。 フィルター シーケンス:
- グローバル フィルターの before コード。
- コントローラー フィルターの before コード。
- アクション メソッド フィルターの before コード。
- アクション メソッド フィルターの after コード。
- コントローラー フィルターの after コード。
- コントローラー フィルターの before コード。
- グローバル フィルターの after コード。
次の例は、同期アクション フィルターに対してフィルター メソッドが実行される順序を示しています。
| Sequence | フィルター スコープ | Filter メソッド |
|---|---|---|
| 1 | Global | OnActionExecuting |
| 2 | Controller | OnActionExecuting |
| 3 | Action | OnActionExecuting |
| 4 | Action | OnActionExecuted |
| 5 | Controller | OnActionExecuted |
| 6 | Global | OnActionExecuted |
コントローラー レベルのフィルター
Controller から継承するすべてのコントローラーには、OnActionExecuting、OnActionExecutionAsync、OnActionExecuted メソッドが含まれます。 これらのメソッドは、特定のアクションに対して実行されるフィルターをラップします。
-
OnActionExecutingは、すべてのアクション フィルターの前に実行します。 -
OnActionExecutedは、すべてのアクション フィルターの後に実行します。 -
OnActionExecutionAsyncは、すべてのアクション フィルターの前に実行します。 アクションのフィルターの後に、nextへの呼び出し後のコードを実行します。
次の ControllerFiltersController クラスは、次のように動作します。
-
SampleActionFilterAttribute([SampleActionFilter]) をコントローラーに適用します。 -
OnActionExecutingとOnActionExecutedがオーバーライドされます。
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");
base.OnActionExecuted(context);
}
public IActionResult Index()
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(Index)}");
return Content("Check the Console.");
}
}
https://localhost:<port>/ControllerFilters に移動すると、次のコードが実行されます。
ControllerFiltersController.OnActionExecutingGlobalSampleActionFilter.OnActionExecutingSampleActionFilterAttribute.OnActionExecutingControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することはできません。 順序については、次のセクションで説明します。
Razor ページについては、フィルターメソッドをオーバーライドして Razor ページフィルターを実装するを参照してください。
既定の順序をオーバーライドする
IOrderedFilter を実装することで、実行の既定の順序をオーバーライドできます。
IOrderedFilter により、実行の順序を決定するために、スコープよりも優先される Order プロパティが公開されます。 より低い Order 値を持つフィルター:
- より高い の値を持つフィルターのそれよりも前に
Orderコードが実行されます。 - より高い の値を持つフィルターのそれよりも後に
Orderコードが実行されます。
コントローラー レベルのフィルターの例では、GlobalSampleActionFilter はグローバル スコープを持つため、コントローラー スコープを持つ SampleActionFilterAttribute の前に実行します。
SampleActionFilterAttribute を最初に実行するようにするには、次のように順序を int.MinValue に設定します。
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
グローバル フィルター GlobalSampleActionFilter を最初に実行するようにするには、次のように Order を int.MinValue に設定します。
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
キャンセルとショートサーキット
フィルター メソッドに提供される Result パラメーターで ResourceExecutingContext プロパティを設定することで、フィルター パイプラインをショートサーキットできます。 たとえば、次のリソース フィルターを使用すると、パイプラインの残りの部分が実行されなくなります。
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
次のコードでは、[ShortCircuitingResourceFilter] と [ResponseHeader] の両方のフィルターが Index アクション メソッドをターゲットにしています。
ShortCircuitingResourceFilterAttribute フィルターは、次のようになります。
- これはリソース フィルターであり、
ResponseHeaderAttributeはアクション フィルターであるため、最初に実行されます。 - パイプラインの残りの部分は迂回されます。
そのため、ResponseHeaderAttribute アクションの場合、Index フィルターが実行されることはありません。
ShortCircuitingResourceFilterAttribute が最初に実行された場合は、両方のフィルターがアクション メソッド レベルで適用されると、この動作が同じになります。
ShortCircuitingResourceFilterAttribute は、フィルターの種類のために最初に実行します。
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
依存関係の挿入
フィルターは種類ごとまたはインスタンスごとに追加できます。 インスタンスが追加される場合、そのインスタンスはすべての要求に対して使用されます。 種類が追加される場合、それは種類でアクティブ化されます。 種類によって稼働するフィルターの意味は次のとおりです。
- インスタンスは要求ごとに作成されます。
- コンストラクターの依存関係は、依存関係の挿入 (DI) によって入力されます。
属性として実装され、コントローラー クラスまたはアクション メソッドに直接追加されるフィルターは、依存関係の挿入 (DI) によって提供されるコンストラクターの依存関係を持つことはできません。 属性には、適用される場所で提供される独自のコンストラクター パラメーターが必要であるため、コンストラクターの依存関係は、DI によって与えられません。
次のフィルターでは、DI から提供されるコンストラクターの依存関係がサポートされます。
上記のフィルターは、コントローラーまたはアクションに適用できます。
ロガーは DI から利用できます。 ただし、ログ目的でのみフィルターを作成し、使用することは避けてください。 組み込みフレームワークのログ機能で通常、ログ記録に必要なものが与えられます。 フィルターに追加されるログ記録:
- ビジネス ドメインの懸念事項やフィルターに固有の動作に焦点を合わせます。
- アクションやその他のフレームワーク イベントはログに記録しないでください。 組み込みのフィルターにより、アクションとフレームワーク イベントが既に記録されています。
ServiceFilterAttribute
サービス フィルターの実装の種類は Program.cs に登録されています。
ServiceFilterAttribute は DI からフィルターのインスタンスを取得します。
次のコードは、DI を使用する LoggingResponseHeaderFilterService クラスを示しています。
public class LoggingResponseHeaderFilterService : IResultFilter
{
private readonly ILogger _logger;
public LoggingResponseHeaderFilterService(
ILogger<LoggingResponseHeaderFilterService> logger) =>
_logger = logger;
public void OnResultExecuting(ResultExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
context.HttpContext.Response.Headers.Add(
nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
}
public void OnResultExecuted(ResultExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
}
}
次のコードでは、LoggingResponseHeaderFilterService が DI コンテナーに追加されます。
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
次のコードでは、ServiceFilter 属性により、DI から LoggingResponseHeaderFilterService フィルターのインスタンスが取得されます。
[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
ServiceFilterAttribute を使用する場合、ServiceFilterAttribute.IsReusable を設定します。
- フィルター インスタンスが、それが作成された要求範囲の外で再利用される可能性があることを示唆します。 ASP.NET Core ランタイムによって保証されない事項は次のとおりです。
- フィルターのインスタンスが 1 つ作成されます。
- フィルターが後の時点で、DI コンテナーから再要求されることはありません。
- シングルトン以外で有効期間があるサービスに依存するフィルターと共にを使用しないでください。
ServiceFilterAttribute は、IFilterFactory を実装します。
IFilterFactory は、CreateInstance インスタンスを作成するために IFilterMetadata メソッドを公開します。
CreateInstance により、DI から指定の型が読み込まれます。
TypeFilterAttribute
TypeFilterAttribute はServiceFilterAttribute と似ていますが、その型は DI コンテナーから直接解決されません。 Microsoft.Extensions.DependencyInjection.ObjectFactory を使って型をインスタンス化します。
TypeFilterAttribute 型は DI コンテナーから直接解決されないためです。
-
TypeFilterAttributeを利用して参照される型は、DI コンテナーに登録する必要がありません。 DI コンテナーによって依存関係が満たされています。 -
TypeFilterAttributeは必要に応じて、型のコンストラクター引数を受け取ることができます。
TypeFilterAttribute を使用する場合、TypeFilterAttribute.IsReusable を設定します。
フィルター インスタンスが、それが作成された要求範囲の外で再利用される可能性があることを示唆します。 ASP.NET Core ランタイムでは、フィルターの 1 インスタンスが作成されるという保証はありません。
シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
次の例は、TypeFilterAttribute を使用して、型に引数を渡す方法を示しています。
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
承認フィルター
承認フィルター:
- フィルター パイプライン内で実行される最初のフィルターです。
- アクション メソッドへのアクセスを制御します。
- before メソッドが与えられ、after メソッドは与えられません。
カスタム承認フィルターには、カスタム承認フレームワークが必要です。 カスタム フィルターを記述するよりも、独自の承認ポリシーを構成するか、カスタム承認ポリシーを記述することを選びます。 組み込み承認フィルター:
- 承認システムを呼び出します。
- 要求を承認しません。
承認フィルター内で例外をスローしません。
- 例外は処理されません。
- 例外フィルターで例外が処理されません。
承認フィルターで例外が発生した場合、チャレンジ発行を検討してください。
承認の詳細については、こちらを参照してください。
リソース フィルター
リソース フィルター:
- IResourceFilter または IAsyncResourceFilter のインターフェイスを実装します。
- 実行により、ほとんどのフィルター パイプラインがラップされます。
- 承認フィルターのみ、リソース フィルターの前に実行されます。
リソースフィルターはほとんどのパイプラインをショートカットするのに役立ちます。 たとえば、キャッシュ フィルターを使用すると、キャッシュ ヒット時のパイプラインの残りの部分を回避できます。
リソース フィルターの例:
DisableFormValueModelBindingAttribute:
- モデル バインドがフォーム データにアクセスすることを禁止します。
- メモリにフォーム データが読み込まれないようにする目的で大きなファイルのアップロードに使用されます。
アクション フィルター
アクションフィルターはPagesには適用されません。 Razor Pages では IPageFilter と IAsyncPageFilter がサポートされています。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。
アクション フィルター:
- IActionFilter または IAsyncActionFilter のインターフェイスを実装します。
- この実行はアクション メソッドの実行を取り囲みます。
次のコードは、サンプル アクション フィルターを示しています。
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
}
}
ActionExecutingContext では次のプロパティが提供されます。
- ActionArguments - アクション メソッドへの入力の読み取りを有効にします。
- Controller - コントローラー インスタンスを操作できます。
-
Result -
Resultを設定すると、アクション メソッドとそれに続くアクション フィルターの実行が中断されます。
アクション メソッドで例外をスローする:
- 後続のフィルターの実行を回避します。
-
Resultの設定とは異なり、結果は成功ではなく、失敗として処理されます。
ActionExecutedContext は、Controller と Result に加え、次のプロパティを提供します。
- Canceled - 別のフィルターによってアクションの実行が中断された場合は、true です。
-
Exception - アクションや以前に実行されたアクション フィルターが例外をスローした場合、null 以外の値になります。 このプロパティを null に設定する:
- 例外が効果的に処理されます。
-
Resultは、アクション メソッドから返されたかのように実行されます。
IAsyncActionFilter の場合、ActionExecutionDelegate の呼び出しによって:
- 後続のすべてのアクション フィルターとアクション メソッドが実行されます。
-
ActionExecutedContextを返します。
ショートサーキットさせるには、Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result を結果インスタンスに割り当て、next(あるいは ActionExecutionDelegate)を呼び出してはいけません。
このフレームワークからは、サブクラス化できる抽象 ActionFilterAttribute が与えられます。
OnActionExecuting アクション フィルターを次の目的で使用できます。
- モデルの状態を検証します。
- 状態が有効でない場合は、エラーを返します。
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Note
[ApiController] 属性で注釈が付けられたコントローラーでは、モデルの状態を自動的に検証し、400 の応答を返します。 詳細については、「自動的な HTTP 400 応答」を参照してください。
OnActionExecuted メソッドは、アクション メソッドの後に実行されます。
- また、Result プロパティを介してアクションの結果を表示したり、操作したりできます。
- Canceled は、アクションの実行が別のフィルターによって短絡された時、true に設定されます。
- アクションまたは後続のアクション フィルターが例外をスローした場合、Exception は null 以外の値に設定されます。
Exceptionを null に設定すると:- 例外が効果的に処理されます。
-
ActionExecutedContext.Resultは、アクション メソッドから通常どおり返されたかのように実行されます。
例外フィルター
例外フィルター:
- IExceptionFilter または IAsyncExceptionFilter を実装します。
- 共通のエラー処理ポリシーを実装する目的で使用できます。
次の例外フィルターのサンプルでは、アプリの開発中に発生する例外に関する詳細を表示します。
public class SampleExceptionFilter : IExceptionFilter
{
private readonly IHostEnvironment _hostEnvironment;
public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
_hostEnvironment = hostEnvironment;
public void OnException(ExceptionContext context)
{
if (!_hostEnvironment.IsDevelopment())
{
// Don't display exception details unless running in Development.
return;
}
context.Result = new ContentResult
{
Content = context.Exception.ToString()
};
}
}
次のコードでは、例外フィルターをテストします。
[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
例外フィルター:
- 前後のイベントがありません。
- OnException または OnExceptionAsync を実装します。
- Razor Page またはコントローラーの作成、モデル バインド、アクション フィルター、またはアクション メソッドで発生する未処理の例外を処理します。
- リソース フィルター、結果フィルター、または MVC 結果の実行で発生した例外はキャッチしません。
例外を処理するには、ExceptionHandled プロパティを true に設定するか、Result プロパティを割り当てます。 これにより、例外の伝達を停止します。 例外フィルターで例外を "成功" に変えることはできません。 それができるのは、アクション フィルターだけです。
例外フィルター:
- アクション内で発生する例外のトラップに適しています。
- エラー処理ミドルウェアほど柔軟ではありません。
例外処理にはミドルウェアを選択してください。 呼び出されたアクション メソッドに基づいてエラー処理が異なる状況でのみ例外フィルターを使用します。 たとえば、アプリには、API エンドポイントとビュー/HTML の両方に対するアクション メソッドがある場合があります。 API エンドポイントは、JSON としてのエラー情報を返す可能性がある一方で、ビュー ベースのアクションがエラー ページを HTML として返す可能性があります。
結果フィルター
結果フィルター:
- インターフェイスを実装します:
- この実行はアクション結果の実行を取り囲みます。
IResultFilter および IAsyncResultFilter
次のコードは、結果フィルターの例を示しています。
public class SampleResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
// Do something before the result executes.
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Do something after the result executes.
}
}
実行されている結果の種類は、アクションに依存します。 ビューを返すアクションには、実行されている ViewResult の一部として、すべての razor 処理が含まれます。 API メソッドは、結果の実行の一部としていくつかのシリアル化を実行できます。 アクション結果に関する詳細を参照してください。
結果フィルターは、アクションまたはアクション フィルターによってアクション結果を生成される場合にのみ実行されます。 結果フィルターは、次の場合には実行されません。
- 承認フィルターまたはリソース フィルターによって、パイプラインがショートサーキットされる。
- アクションの結果を生成することで、例外フィルターによって例外が処理されます。
Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting メソッドは、Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel を true に設定することで、アクションの結果と後続の結果フィルターの実行をショートサーキットできます。 ショートサーキットする場合は、空の応答が生成されないように応答オブジェクトに記述します。
IResultFilter.OnResultExecuting での例外のスロー:
- アクション結果と後続フィルターの実行を回避します。
- 結果は成功ではなく、失敗として処理されます。
Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted メソッドが実行されたとき、おそらく応答は既にクライアントに送信されています。 応答が既にクライアントに送信されていた場合は、それを変更することはできません。
別のフィルターによってアクション結果の実行がショートサーキットされた場合、ResultExecutedContext.Canceled は true に設定されます。
アクションの結果または後続の結果フィルターが例外をスローした場合、ResultExecutedContext.Exception は null 以外の値に設定されます。
Exception を null に設定すると、例外を効果的に処理し、パイプラインの後方で例外が再スローされるのを防ぐことができます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。
IAsyncResultFilter の場合、await next で ResultExecutionDelegate を呼び出すと、後続のすべての結果フィルターとアクションの結果が実行されます。 ショートサーキットするには、ResultExecutingContext.Cancel を true に設定します。ResultExecutionDelegate は呼び出さないでください。
public class SampleAsyncResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is not EmptyResult)
{
await next();
}
else
{
context.Cancel = true;
}
}
}
このフレームワークからは、サブクラス化できる抽象 ResultFilterAttribute が与えられます。 前に示した ResponseHeaderAttribute クラスは、結果フィルター属性の一例です。
IAlwaysRunResultFilter および IAsyncAlwaysRunResultFilter
IAlwaysRunResultFilter および IAsyncAlwaysRunResultFilter インターフェイスでは、すべてのアクションの結果に対して実行される IResultFilter の実装が宣言されます。 これには、以下によって生成されるアクションの結果が含まれます。
- ショートサーキットが行われる承認フィルターとリソース フィルター。
- 例外フィルター。
たとえば、次のフィルターは常に実行され、コンテンツ ネゴシエーションが失敗した場合に ObjectResult 状態コードを使ってアクションの結果 () を設定します。
public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult
&& statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
{
context.Result = new ObjectResult("Unprocessable")
{
StatusCode = StatusCodes.Status422UnprocessableEntity
};
}
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
IFilterFactory
IFilterFactory は、IFilterMetadata を実装します。 そのため、IFilterFactory インスタンスはフィルター パイプライン内の任意の場所で IFilterMetadata インスタンスとして使用できます。 ランタイムでは、フィルターを呼び出す準備をする際、IFilterFactory へのキャストが試行されます。 そのキャストが成功した場合、呼び出される CreateInstance インスタンスを作成するために IFilterMetadata メソッドが呼び出されます。 これにより、アプリの起動時に正確なフィルター パイプラインを明示的に設定する必要がないため、柔軟なデザインが可能になります。
IFilterFactory.IsReusable:
- ファクトリによって作成されたフィルター インスタンスは、それが作成された要求範囲の外で使用される可能性があることがファクトリによって示されています。
- シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
ASP.NET Core ランタイムで保証されないこと:
- フィルターのインスタンスが 1 つ作成されます。
- フィルターが後の時点で、DI コンテナーから再要求されることはありません。
Warning
フィルターのソースが明確であり、フィルターがステートレスであり、かつ複数の HTTP リクエストで安全に使用できる場合にのみ、IFilterFactory.IsReusable を true で返すように設定します。 たとえば、IFilterFactory.IsReusable で true が返される場合、範囲付きまたは一時的として登録されているフィルターを DI から返さないようにします。
フィルターを作成するための別の方法として、カスタムの属性の実装で IFilterFactory を実装できます。
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
フィルターは次のコードで適用されます。
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
属性上に実装された IFilterFactory
IFilterFactory を実装するフィルターは次のようなフィルターに便利です。
- パラメーターの渡しを要求しない。
- DI で満たす必要があるコンストラクター依存関係があります。
TypeFilterAttribute は、IFilterFactory を実装します。
IFilterFactory は、CreateInstance インスタンスを作成するために IFilterMetadata メソッドを公開します。
CreateInstance により、サービス コンテナー (DI) から指定の型が読み込まれます。
public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
public SampleActionTypeFilterAttribute()
: base(typeof(InternalSampleActionFilter)) { }
private class InternalSampleActionFilter : IActionFilter
{
private readonly ILogger<InternalSampleActionFilter> _logger;
public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
_logger = logger;
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
}
}
}
次のコードでは、フィルターを適用する 3 つの手法を確認できます。
[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");
前のコードでは、フィルターを適用するための最初の方法をお勧めします。
フィルター パイプラインでミドルウェアを使用する
リソースフィルターは、パイプライン内の後に続くすべての処理を囲むように、ミドルウェアのように機能します。 ただし、フィルターはランタイムの一部である点がミドルウェアとは異なります。つまり、それらはコンテキストとコンストラクトにアクセスすることができます。
ミドルウェアをフィルターとして使用するには、フィルター パイプラインに挿入するミドルウェアを指定する Configure メソッドを使用して型を作成します。 次の例では、ミドルウェアを使用して応答ヘッダーを設定します。
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
MiddlewareFilterAttribute を使用し、ミドルウェアを実行します。
[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
ミドルウェア フィルターは、モデル バインドの前とパイプラインの残りの部分の後で、リソース フィルターと同じ段階でフィルター パイプラインで実行されます。
スレッドの安全性
その の代わりにフィルターの "Add" を Type に渡すとき、フィルターはシングルトンであり、スレッドセーフではありません。
その他のリソース
作成者: Kirk Larkin、Rick Anderson、Tom Dykstra、Steve Smith
ASP.NET Core でフィルターを使用すると、要求処理パイプラインの特定のステージの前または後にコードを実行できます。
組み込みのフィルターでは次のようなタスクが処理されます。
- 許可。ユーザーに許可が与えられていないリソースの場合、アクセスを禁止する。
- 応答キャッシュ。要求パイプラインを迂回し、キャッシュされている応答を返す。
横断的な問題を処理するカスタム フィルターを作成できます。 横断的な問題の例には、エラー処理、キャッシュ、構成、認証、ログなどがあります。 フィルターにより、コードの重複が回避されます。 たとえば、エラー処理例外フィルターではエラー処理を統合できます。
このドキュメントは、Razor Pages、API コントローラー、ビューのあるコントローラーに当てはまります。 フィルターは、Razor コンポーネントでは直接機能しません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。
- コンポーネントがページまたはビューに埋め込まれている。
- ページまたはコントローラーとビューでフィルターが使用されている。
サンプルを表示またはダウンロードします (ダウンロード方法)。
フィルターのしくみ
ASP.NET Core のアクション呼び出しパイプライン内で実行されるフィルターは、フィルター パイプラインと呼ばれることがあります。 フィルター パイプラインは、ASP.NET Core が実行するアクションを選択した後に実行されます。
フィルター タイプ
フィルターの種類はそれぞれ、フィルター パイプラインの異なるステージで実行されます。
承認フィルターは、最初に実行され、ユーザーが要求に対して承認されているかどうかを判断するために使用されます。 承認フィルターでは、要求が承認されていない場合、パイプラインがショートサーキットされます。
-
- 承認後に実行されます。
-
OnResourceExecuting は、フィルター パイプラインの残りの部分の前にコードを実行します。 たとえば、
OnResourceExecutingでは、モデル バインディングの前にコードが実行されます。 - OnResourceExecuted、パイプラインの残りの部分が完了した後にコードを実行します。
-
- アクション メソッドが呼び出される直前と直後にコードを実行します。
- アクションに渡される引数を変更できます。
- アクションから返された結果を変更できます。
- Pages ではサポートされてRazor。
例外フィルターでは、応答本文への書き込みが行われる前に発生する未処理の例外にグローバル ポリシーが適用されます。
結果フィルターでは、アクション結果の実行の直前と直後にコードが実行されます。 アクション メソッドが正常に実行された場合にのみ実行されます。 それらは、ビューやフォーマッタの実行に必要なロジックにとって便利です。
フィルターの種類がフィルター パイプラインでどのように連携しているかを、次の図に示します。
Implementation
フィルターは、異なるインターフェイス定義を介して、同期と非同期の実装をサポートします。
同期フィルターでは、そのパイプライン ステージの前と後にコードが実行されます。 たとえば、OnActionExecuting はアクション メソッドの呼び出し前に呼び出されます。 OnActionExecuted は、アクション メソッドが戻った後に呼び出されます。
public class MySampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
}
前のコードでは、MyDebug はサンプル ダウンロードのユーティリティ関数です。
非同期フィルターでは、On-Stage-ExecutionAsync メソッドが定義されます。
OnActionExecutionAsync の例を次に示します。
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
// Do something before the action executes.
// next() calls the action method.
var resultContext = await next();
// resultContext.Result is set.
// Do something after the action executes.
}
}
上記のコードでは、SampleAsyncActionFilter にアクション メソッドを実行する ActionExecutionDelegate (next) があります。
複数のフィルター ステージ
複数のフィルター ステージのためのインターフェイスを 1 つのクラスで実装できます。 たとえば、ActionFilterAttribute クラスでは次のものが実装されます。
フィルター インターフェイスの同期と非同期バージョンの両方ではなく、いずれかを実装します。 ランタイムは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。 していない場合は、同期インターフェイスのメソッドを呼び出します。 非同期インターフェイスと同期インターフェイスの両方が 1 つのクラスで実装される場合、非同期メソッドのみが呼び出されます。 ActionFilterAttribute などの抽象クラスを使用する場合は、同期メソッドのみをオーバーライドするか、フィルターの種類ごとに非同期メソッドをオーバーライドします。
組み込みのフィルター属性
ASP.NET Core には、サブクラスを作成したり、カスタマイズしたりできる組み込みの属性ベースのフィルターが含まれます。 たとえば、次の結果フィルターは、応答にヘッダーを追加します。
public class AddHeaderAttribute : ResultFilterAttribute
{
private readonly string _name;
private readonly string _value;
public AddHeaderAttribute(string name, string value)
{
_name = name;
_value = value;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add( _name, new string[] { _value });
base.OnResultExecuting(context);
}
}
上記の例のように、属性によってフィルターは引数を受け取ることができます。
AddHeaderAttribute をコントローラーまたはアクション メソッドに適用し、HTTP ヘッダーの名前と値を指定します。
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
ブラウザー開発者ツール などのツールを使用して、ヘッダーを調べます。
[応答ヘッダー] の下に author: Rick Anderson が表示されます。
次のコードでは、次のことを行う ActionFilterAttribute が実装されます。
- 構成システムからタイトルと名前を読み取ります。 前のサンプルとは異なり、次のコードでは、フィルター パラメーターをコードに追加する必要はありません。
- 応答ヘッダーにタイトルと名前を追加します。
public class MyActionFilterAttribute : ActionFilterAttribute
{
private readonly PositionOptions _settings;
public MyActionFilterAttribute(IOptions<PositionOptions> options)
{
_settings = options.Value;
Order = 1;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(_settings.Title,
new string[] { _settings.Name });
base.OnResultExecuting(context);
}
}
構成オプションは、構成システムからオプション パターンを使用して指定されます。 たとえば、appsettings.json ファイルから:
{
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
StartUp.ConfigureServices では、次のことが行われます。
-
PositionOptions構成領域を使用して"Position"クラスをサービス コンテナーに追加します。 -
MyActionFilterAttributeをサービス コンテナーに追加します。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(
Configuration.GetSection("Position"));
services.AddScoped<MyActionFilterAttribute>();
services.AddControllersWithViews();
}
次のコードは PositionOptions クラスを示しています。
public class PositionOptions
{
public string Title { get; set; }
public string Name { get; set; }
}
次のコードでは、MyActionFilterAttribute メソッドに Index2 が適用されています。
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ServiceFilter(typeof(MyActionFilterAttribute))]
public IActionResult Index2()
{
return Content("Header values by configuration.");
}
Response Headers の下に、author: Rick Anderson および Editor: Joe Smith が表示され、Sample/Index2 エンドポイントが呼び出されます。
次のコードでは、MyActionFilterAttribute と AddHeaderAttribute が Razor ページに適用されます。
[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Razor ページ ハンドラー メソッドにフィルターを適用することはできません。 これらは、Razor ページ モデルに適用するか、グローバルに適用することが可能です。
フィルター インターフェイスのいくつかには対応する属性があり、カスタムの実装に基底クラスとして使用できます。
フィルター属性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
フィルターのスコープと実行の順序
フィルターは、3 つのスコープのいずれかでパイプラインに追加することができます。
- コントローラー アクションでの属性の使用。 フィルター属性を Razor Pages ハンドラー メソッドに適用することはできません。
- コントローラーまたは Razor ページでの属性の使用。
- 次のコードのように、すべてのコントローラー、アクション、および Razor Pages に対してグローバルに:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
実行の既定の順序
パイプラインの特定のステージに対して複数のフィルターがある場合に、スコープがフィルターの実行の既定の順序を決定します。 グローバル フィルターがクラス フィルターを囲み、クラス フィルターがメソッド フィルターを囲みます。
フィルターの入れ子の結果として、フィルターの after コードが before コードと逆の順序で実行されます。 フィルター シーケンス:
- グローバル フィルターの before コード。
- コントローラーおよびページフィルターのRazorコード。
- アクション メソッド フィルターの before コード。
- アクション メソッド フィルターの after コード。
- コントローラーおよび ページ フィルターの Razor コード。
- コントローラーおよびページフィルターのRazorコード。
- グローバル フィルターの after コード。
次の例は、同期アクション フィルターに対してフィルター メソッドが呼び出される順序を示しています。
| Sequence | フィルター スコープ | Filter メソッド |
|---|---|---|
| 1 | Global | OnActionExecuting |
| 2 | コントローラーまたは Razor ページ | OnActionExecuting |
| 3 | Method | OnActionExecuting |
| 4 | Method | OnActionExecuted |
| 5 | コントローラーまたは Razor ページ | OnActionExecuted |
| 6 | Global | OnActionExecuted |
コントローラー レベルのフィルター
Controller 基底クラスから継承するすべてのコントローラーには、Controller.OnActionExecuting、Controller.OnActionExecutionAsync、Controller.OnActionExecutedOnActionExecuted メソッドが含まれます。 これらのメソッドは次のとおりです。
- 特定のアクションに対して実行されるフィルターをラップします。
-
OnActionExecutingは、あらゆるアクション フィルターの前に呼び出されます。 -
OnActionExecutedは、あらゆるアクション フィルターの後に呼び出されます。 -
OnActionExecutionAsyncは、あらゆるアクション フィルターの前に呼び出されます。nextの後のフィルターのコードは、アクション メソッドの後に実行されます。
たとえば、ダウンロード サンプルで、MySampleActionFilter は起動中、グローバルに適用されます。
TestController:
-
SampleActionFilterAttribute([SampleActionFilter]) がFilterTest2アクションに適用されます。 -
OnActionExecutingとOnActionExecutedがオーバーライドされます。
public class TestController : Controller
{
[SampleActionFilter(Order = int.MinValue)]
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
MyDisplayRouteInfo は Rick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。
https://localhost:5001/Test/FilterTest2 に移動すると、次のコードが実行されます。
TestController.OnActionExecutingMySampleActionFilter.OnActionExecutingSampleActionFilterAttribute.OnActionExecutingTestController.FilterTest2
SampleActionFilterAttribute.OnActionExecuted
MySampleActionFilter.OnActionExecuted
TestController.OnActionExecuted
コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することはできません。 順序については、次のセクションで説明します。
Razor ページについては、フィルターメソッドをオーバーライドして Razor ページフィルターを実装するを参照してください。
既定の順序のオーバーライド
IOrderedFilter を実装することで、実行の既定の順序をオーバーライドできます。
IOrderedFilter により、実行の順序を決定するために、スコープよりも優先される Order プロパティが公開されます。 より低い Order 値を持つフィルター:
- より高い の値を持つフィルターのそれよりも前に
Orderコードが実行されます。 - より高い の値を持つフィルターのそれよりも後に
Orderコードが実行されます。
Order プロパティは、次のコンストラクター パラメーターで設定できます。
[SampleActionFilter(Order = int.MinValue)]
次のコントローラーの 2 つのアクションフィルターについて考えてみましょう。
[MyAction2Filter]
public class Test2Controller : Controller
{
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
グローバル フィルターが次のように StartUp.ConfigureServices に追加されます。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
3 つのフィルターは、次の順序で実行されます。
Test2Controller.OnActionExecutingMySampleActionFilter.OnActionExecutingMyAction2FilterAttribute.OnActionExecutingTest2Controller.FilterTest2
MyAction2FilterAttribute.OnResultExecuting
MySampleActionFilter.OnActionExecuted
Test2Controller.OnActionExecuted
フィルターの実行順序を決定するときに、Order プロパティによりスコープがオーバーライドされます。 最初に順序でフィルターが並べ替えられ、次に同じ順位の優先度を決めるためにスコープが使用されます。 組み込みのフィルターはすべて IOrderedFilter を実装し、既定の Order 値を 0 に設定します。 既に説明したように、コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。組み込みのフィルターの場合は、Order が 0 以外の値に設定されている場合を除き、スコープによって順序が決定されます。
上記のコードにおいて、MySampleActionFilter にはグローバル スコープがあるため、コントローラー スコープを持つ MyAction2FilterAttribute の前で実行されます。
MyAction2FilterAttribute を最初に実行するようにするには、int.MinValue に対して順序を設定します。
[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
グローバル フィルター MySampleActionFilter を最初に実行するようにするには、次のように Order を int.MinValue に設定します。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter),
int.MinValue);
});
}
キャンセルとショートサーキット
フィルター メソッドに提供される Result パラメーターで ResourceExecutingContext プロパティを設定することで、フィルター パイプラインをショートサーキットできます。 たとえば、次のリソース フィルターを使用すると、パイプラインの残りの部分が実行されなくなります。
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult()
{
Content = "Resource unavailable - header not set."
};
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
次のコードでは、ShortCircuitingResourceFilter と AddHeader の両方のフィルターが SomeResource アクション メソッドをターゲットにしています。
ShortCircuitingResourceFilter:
- これはリソース フィルターであり、
AddHeaderはアクション フィルターであるため、最初に実行されます。 - パイプラインの残りの部分は迂回されます。
そのため、AddHeader アクションの場合、SomeResource フィルターが実行されることはありません。
ShortCircuitingResourceFilter が最初に実行された場合は、両方のフィルターがアクション メソッド レベルで適用されると、この動作が同じになります。 そのフィルターの種類が原因で、あるいは ShortCircuitingResourceFilter プロパティの明示的な使用により、Order が最初に実行されます。
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ServiceFilter(typeof(MyActionFilterAttribute))]
public IActionResult Index2()
{
return Content("Header values by configuration.");
}
[ShortCircuitingResourceFilter]
public IActionResult SomeResource()
{
return Content("Successful access to resource - header is set.");
}
[AddHeaderWithFactory]
public IActionResult HeaderWithFactory()
{
return Content("Examine the headers using the F12 developer tools.");
}
}
依存関係の挿入
フィルターは種類ごとまたはインスタンスごとに追加できます。 インスタンスが追加される場合、そのインスタンスはすべての要求に対して使用されます。 種類が追加される場合、それは種類でアクティブ化されます。 種類によって稼働するフィルターの意味は次のとおりです。
- インスタンスは要求ごとに作成されます。
- コンストラクターの依存関係は、依存関係の挿入 (DI) によって入力されます。
属性として実装され、コントローラー クラスまたはアクション メソッドに直接追加されるフィルターは、依存関係の挿入 (DI) によって提供されるコンストラクターの依存関係を持つことはできません。 コンストラクターの依存関係は、次の理由から DI によって与えられません。
- 属性には、適用される場所で提供される独自のコンストラクター パラメーターが必要です。
- これは、属性のしくみの制限です。
次のフィルターでは、DI から提供されるコンストラクターの依存関係がサポートされます。
上記のフィルターは、コントローラーまたはアクション メソッドに適用できます。
ロガーは DI から利用できます。 ただし、ログ目的でのみフィルターを作成し、使用することは避けてください。 組み込みフレームワークのログ機能で通常、ログ記録に必要なものが与えられます。 フィルターに追加されるログ記録:
- ビジネス ドメインの懸念事項やフィルターに固有の動作に焦点を合わせます。
- アクションやその他のフレームワーク イベントはログに記録しないでください。 組み込みのフィルターでは、アクションとフレームワーク イベントが記録されます。
ServiceFilterAttribute
サービス フィルターの実装の種類は ConfigureServices に登録されています。
ServiceFilterAttribute は DI からフィルターのインスタンスを取得します。
このコードでは、AddHeaderResultServiceFilter が示されています。
public class AddHeaderResultServiceFilter : IResultFilter
{
private ILogger _logger;
public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
}
public void OnResultExecuting(ResultExecutingContext context)
{
var headerName = "OnResultExecuting";
context.HttpContext.Response.Headers.Add(
headerName, new string[] { "ResultExecutingSuccessfully" });
_logger.LogInformation("Header added: {HeaderName}", headerName);
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Can't add to headers here because response has started.
_logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
}
}
次のコードでは、AddHeaderResultServiceFilter が DI コンテナーに追加されます。
public void ConfigureServices(IServiceCollection services)
{
// Add service filters.
services.AddScoped<AddHeaderResultServiceFilter>();
services.AddScoped<SampleActionFilterAttribute>();
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
"Result filter added to MvcOptions.Filters")); // An instance
options.Filters.Add(typeof(MySampleActionFilter)); // By type
options.Filters.Add(new SampleGlobalActionFilter()); // An instance
});
}
次のコードでは、ServiceFilter 属性により、DI から AddHeaderResultServiceFilter フィルターのインスタンスが取得されます。
[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
return View();
}
ServiceFilterAttribute を使用する場合、ServiceFilterAttribute.IsReusable を設定します。
フィルター インスタンスが、それが作成された要求範囲の外で再利用される可能性があることを示唆します。 ASP.NET Core ランタイムで保証されないこと:
- フィルターのインスタンスが 1 つ作成されます。
- フィルターが後の時点で、DI コンテナーから再要求されることはありません。
シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
ServiceFilterAttribute は、IFilterFactory を実装します。
IFilterFactory は、CreateInstance インスタンスを作成するために IFilterMetadata メソッドを公開します。
CreateInstance により、DI から指定の型が読み込まれます。
TypeFilterAttribute
TypeFilterAttribute はServiceFilterAttribute と似ていますが、その型は DI コンテナーから直接解決されません。 Microsoft.Extensions.DependencyInjection.ObjectFactory を使って型をインスタンス化します。
TypeFilterAttribute 型は DI コンテナーから直接解決されないためです。
-
TypeFilterAttributeを利用して参照される型は、DI コンテナーに登録する必要がありません。 DI コンテナーによって依存関係が満たされています。 -
TypeFilterAttributeは必要に応じて、型のコンストラクター引数を受け取ることができます。
TypeFilterAttribute を使用する場合、TypeFilterAttribute.IsReusable を設定します。
フィルター インスタンスが、それが作成された要求範囲の外で再利用される可能性があることを示唆します。 ASP.NET Core ランタイムでは、フィルターの 1 インスタンスが作成されるという保証はありません。
シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
次の例は、TypeFilterAttribute を使用して、型に引数を渡す方法を示しています。
[TypeFilter(typeof(LogConstantFilter),
Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
return Content($"Hi {name}");
}
承認フィルター
承認フィルター:
- フィルター パイプライン内で実行される最初のフィルターです。
- アクション メソッドへのアクセスを制御します。
- before メソッドが与えられ、after メソッドは与えられません。
カスタム承認フィルターには、カスタム承認フレームワークが必要です。 カスタム フィルターを記述するよりも、独自の承認ポリシーを構成するか、カスタム承認ポリシーを記述することを選びます。 組み込み承認フィルター:
- 承認システムを呼び出します。
- 要求を承認しません。
承認フィルター内で例外をスローしません。
- 例外は処理されません。
- 例外フィルターで例外が処理されません。
承認フィルターで例外が発生した場合、チャレンジ発行を検討してください。
承認の詳細については、こちらを参照してください。
リソース フィルター
リソース フィルター:
- IResourceFilter または IAsyncResourceFilter のインターフェイスを実装します。
- 実行により、ほとんどのフィルター パイプラインがラップされます。
- 承認フィルターのみ、リソース フィルターの前に実行されます。
リソースフィルターはほとんどのパイプラインをショートカットするのに役立ちます。 たとえば、キャッシュ フィルターを使用すると、キャッシュ ヒット時のパイプラインの残りの部分を回避できます。
リソース フィルターの例:
DisableFormValueModelBindingAttribute:
- モデル バインドがフォーム データにアクセスすることを禁止します。
- メモリにフォーム データが読み込まれないようにする目的で大きなファイルのアップロードに使用されます。
アクション フィルター
アクションフィルターはPagesには適用されません。 Razor Pages では IPageFilter と IAsyncPageFilter がサポートされています。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。
アクション フィルター:
- IActionFilter または IAsyncActionFilter のインターフェイスを実装します。
- この実行はアクション メソッドの実行を取り囲みます。
次のコードは、サンプル アクション フィルターを示しています。
public class MySampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
}
ActionExecutingContext では次のプロパティが提供されます。
- ActionArguments - アクション メソッドへの入力の読み取りを有効にします。
- Controller - コントローラー インスタンスを操作できます。
-
Result -
Resultを設定すると、アクション メソッドとそれに続くアクション フィルターの実行が中断されます。
アクション メソッドで例外をスローする:
- 後続のフィルターの実行を回避します。
-
Resultの設定とは異なり、結果は成功ではなく、失敗として処理されます。
ActionExecutedContext は、Controller と Result に加え、次のプロパティを提供します。
Canceled - 別のフィルターによってアクションの実行が中断された場合は、true です。
Exception - アクションや以前に実行されたアクション フィルターが例外をスローした場合、null 以外の値になります。 このプロパティを null に設定する:
- 例外が効果的に処理されます。
-
Resultは、アクション メソッドから返されたかのように実行されます。
IAsyncActionFilter の場合、ActionExecutionDelegate の呼び出しによって:
- 後続のすべてのアクション フィルターとアクション メソッドが実行されます。
-
ActionExecutedContextを返します。
ショートサーキットさせるには、Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result を結果インスタンスに割り当て、next(あるいは ActionExecutionDelegate)を呼び出してはいけません。
このフレームワークからは、サブクラス化できる抽象 ActionFilterAttribute が与えられます。
OnActionExecuting アクション フィルターを次の目的で使用できます。
- モデルの状態を検証します。
- 状態が有効でない場合は、エラーを返します。
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext
context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(
context.ModelState);
}
}
Note
[ApiController] 属性で注釈が付けられたコントローラーでは、モデルの状態を自動的に検証し、400 の応答を返します。 詳細については、「自動的な HTTP 400 応答」を参照してください。
OnActionExecuted メソッドは、アクション メソッドの後に実行されます。
また、Result プロパティを介してアクションの結果を表示したり、操作したりできます。
Canceled は、アクションの実行が別のフィルターによって短絡された時、true に設定されます。
アクションまたは後続のアクション フィルターが例外をスローした場合、Exception は null 以外の値に設定されます。
Exceptionを null に設定すると:- 例外が効果的に処理されます。
-
ActionExecutedContext.Resultは、アクション メソッドから通常どおり返されたかのように実行されます。
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext
context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(
context.ModelState);
}
}
public override void OnActionExecuted(ActionExecutedContext
context)
{
var result = context.Result;
// Do something with Result.
if (context.Canceled == true)
{
// Action execution was short-circuited by another filter.
}
if(context.Exception != null)
{
// Exception thrown by action or action filter.
// Set to null to handle the exception.
context.Exception = null;
}
base.OnActionExecuted(context);
}
}
例外フィルター
例外フィルター:
- IExceptionFilter または IAsyncExceptionFilter を実装します。
- 共通のエラー処理ポリシーを実装する目的で使用できます。
次の例外フィルターのサンプルでは、カスタムのエラー ビューを使用して、アプリの開発中に発生する例外に関する詳細を表示します。
public class CustomExceptionFilter : IExceptionFilter
{
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly IModelMetadataProvider _modelMetadataProvider;
public CustomExceptionFilter(
IWebHostEnvironment hostingEnvironment,
IModelMetadataProvider modelMetadataProvider)
{
_hostingEnvironment = hostingEnvironment;
_modelMetadataProvider = modelMetadataProvider;
}
public void OnException(ExceptionContext context)
{
if (!_hostingEnvironment.IsDevelopment())
{
return;
}
var result = new ViewResult {ViewName = "CustomError"};
result.ViewData = new ViewDataDictionary(_modelMetadataProvider,
context.ModelState);
result.ViewData.Add("Exception", context.Exception);
// TODO: Pass additional detailed data via ViewData
context.Result = result;
}
}
次のコードでは、例外フィルターをテストします。
[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
[AddHeader("Failing Controller",
"Won't appear when exception is handled")]
public IActionResult Index()
{
throw new Exception("Testing custom exception filter.");
}
}
例外フィルター:
- 前後のイベントがありません。
- OnException または OnExceptionAsync を実装します。
- Razor Page またはコントローラーの作成、モデル バインド、アクション フィルター、またはアクション メソッドで発生する未処理の例外を処理します。
- リソース フィルター、結果フィルター、または MVC 結果の実行で発生した例外はキャッチしません。
例外を処理するには、ExceptionHandled プロパティを true に設定するか、Result プロパティを割り当てます。 これにより、例外の伝達を停止します。 例外フィルターで例外を "成功" に変えることはできません。 それができるのは、アクション フィルターだけです。
例外フィルター:
- アクション内で発生する例外のトラップに適しています。
- エラー処理ミドルウェアほど柔軟ではありません。
例外処理にはミドルウェアを選択してください。 呼び出されたアクション メソッドに基づいてエラー処理が異なる状況でのみ例外フィルターを使用します。 たとえば、アプリには、API エンドポイントとビュー/HTML の両方に対するアクション メソッドがある場合があります。 API エンドポイントは、JSON としてのエラー情報を返す可能性がある一方で、ビュー ベースのアクションがエラー ページを HTML として返す可能性があります。
結果フィルター
結果フィルター:
- インターフェイスを実装します:
- この実行はアクション結果の実行を取り囲みます。
IResultFilter および IAsyncResultFilter
次は、HTTP ヘッダーを追加する結果フィルターのコードです。
public class AddHeaderResultServiceFilter : IResultFilter
{
private ILogger _logger;
public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
}
public void OnResultExecuting(ResultExecutingContext context)
{
var headerName = "OnResultExecuting";
context.HttpContext.Response.Headers.Add(
headerName, new string[] { "ResultExecutingSuccessfully" });
_logger.LogInformation("Header added: {HeaderName}", headerName);
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Can't add to headers here because response has started.
_logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
}
}
実行されている結果の種類は、アクションに依存します。 ビューを返すアクションには、実行されている ViewResult の一部として、すべての razor 処理が含まれます。 API メソッドは、結果の実行の一部としていくつかのシリアル化を実行できます。 アクション結果に関する詳細を参照してください。
結果フィルターは、アクションまたはアクション フィルターによってアクション結果を生成される場合にのみ実行されます。 結果フィルターは、次の場合には実行されません。
- 承認フィルターまたはリソース フィルターによって、パイプラインがショートサーキットされる。
- アクションの結果を生成することで、例外フィルターによって例外が処理されます。
Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting メソッドは、Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel を true に設定することで、アクションの結果と後続の結果フィルターの実行をショートサーキットできます。 ショートサーキットする場合は、空の応答が生成されないように応答オブジェクトに記述します。
IResultFilter.OnResultExecuting での例外のスロー:
- アクション結果と後続フィルターの実行を回避します。
- 結果は成功ではなく、失敗として処理されます。
Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted メソッドが実行されたとき、おそらく応答は既にクライアントに送信されています。 応答が既にクライアントに送信されていた場合は、それを変更することはできません。
別のフィルターによってアクション結果の実行がショートサーキットされた場合、ResultExecutedContext.Canceled は true に設定されます。
アクションの結果または後続の結果フィルターが例外をスローした場合、ResultExecutedContext.Exception は null 以外の値に設定されます。
Exception を null に設定すると、例外を効果的に処理し、パイプラインの後方で例外が再スローされるのを防ぐことができます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。
IAsyncResultFilter の場合、await next で ResultExecutionDelegate を呼び出すと、後続のすべての結果フィルターとアクションの結果が実行されます。 ショートサーキットするには、ResultExecutingContext.Cancel を true に設定します。ResultExecutionDelegate は呼び出さないでください。
public class MyAsyncResponseFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context,
ResultExecutionDelegate next)
{
if (!(context.Result is EmptyResult))
{
await next();
}
else
{
context.Cancel = true;
}
}
}
このフレームワークからは、サブクラス化できる抽象 ResultFilterAttribute が与えられます。 前に示した AddHeaderAttribute クラスは、結果フィルター属性の一例です。
IAlwaysRunResultFilter および IAsyncAlwaysRunResultFilter
IAlwaysRunResultFilter および IAsyncAlwaysRunResultFilter インターフェイスでは、すべてのアクションの結果に対して実行される IResultFilter の実装が宣言されます。 これには、以下によって生成されるアクションの結果が含まれます。
- ショートサーキットが行われる承認フィルターとリソース フィルター。
- 例外フィルター。
たとえば、次のフィルターは常に実行され、コンテンツ ネゴシエーションが失敗した場合に ObjectResult 状態コードを使ってアクションの結果 () を設定します。
public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult &&
statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
{
context.Result = new ObjectResult("Can't process this!")
{
StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
};
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
IFilterFactory
IFilterFactory は、IFilterMetadata を実装します。 そのため、IFilterFactory インスタンスはフィルター パイプライン内の任意の場所で IFilterMetadata インスタンスとして使用できます。 ランタイムでは、フィルターを呼び出す準備をする際、IFilterFactory へのキャストが試行されます。 そのキャストが成功した場合、呼び出される CreateInstance インスタンスを作成するために IFilterMetadata メソッドが呼び出されます。 これにより、アプリの起動時に正確なフィルター パイプラインを明示的に設定する必要がないため、柔軟なデザインが可能になります。
IFilterFactory.IsReusable:
- ファクトリによって作成されたフィルター インスタンスは、それが作成された要求範囲の外で使用される可能性があることがファクトリによって示されています。
- シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください。
ASP.NET Core ランタイムで保証されないこと:
- フィルターのインスタンスが 1 つ作成されます。
- フィルターが後の時点で、DI コンテナーから再要求されることはありません。
Warning
フィルターのソースが明確であり、フィルターがステートレスであり、かつ複数の HTTP リクエストで安全に使用できる場合にのみ、IFilterFactory.IsReusable を true で返すように設定します。 たとえば、IFilterFactory.IsReusable で true が返される場合、範囲付きまたは一時的として登録されているフィルターを DI から返さないようにします。
フィルターを作成するための別の方法として、カスタムの属性の実装で IFilterFactory を実装できます。
public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
// Implement IFilterFactory
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return new InternalAddHeaderFilter();
}
private class InternalAddHeaderFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(
"Internal", new string[] { "My header" });
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
フィルターは次のコードで適用されます。
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ServiceFilter(typeof(MyActionFilterAttribute))]
public IActionResult Index2()
{
return Content("Header values by configuration.");
}
[ShortCircuitingResourceFilter]
public IActionResult SomeResource()
{
return Content("Successful access to resource - header is set.");
}
[AddHeaderWithFactory]
public IActionResult HeaderWithFactory()
{
return Content("Examine the headers using the F12 developer tools.");
}
}
ダウンロード サンプルを実行することで、上記のコードをテストします。
- F12 開発者ツールを呼び出します。
-
https://localhost:5001/Sample/HeaderWithFactoryに移動します。
F12 開発者ツールでは、サンプル コードによって追加された次の応答ヘッダーが表示されます。
-
著者:
Rick Anderson -
globaladdheader:
Result filter added to MvcOptions.Filters -
国内:
My header
上記のコードでは、internal:My header という応答ヘッダーが作成されます。
属性上に実装された IFilterFactory
IFilterFactory を実装するフィルターは次のようなフィルターに便利です。
- パラメーターの渡しを要求しない。
- DI で満たす必要があるコンストラクター依存関係があります。
TypeFilterAttribute は、IFilterFactory を実装します。
IFilterFactory は、CreateInstance インスタンスを作成するために IFilterMetadata メソッドを公開します。
CreateInstance により、サービス コンテナー (DI) から指定の型が読み込まれます。
public class SampleActionFilterAttribute : TypeFilterAttribute
{
public SampleActionFilterAttribute()
:base(typeof(SampleActionFilterImpl))
{
}
private class SampleActionFilterImpl : IActionFilter
{
private readonly ILogger _logger;
public SampleActionFilterImpl(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.LogInformation("SampleActionFilterAttribute.OnActionExecuting");
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
}
}
}
次のコードでは、[SampleActionFilter] を適用する 3 つの手法を確認できます。
[SampleActionFilter]
public IActionResult FilterTest()
{
return Content("From FilterTest");
}
[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
return Content("From TypeFilterTest");
}
// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>'
// has been registered. Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
return Content("From ServiceFilterTest");
}
上記のコードでは、[SampleActionFilter] を適用する方法としては、SampleActionFilter でメソッドを装飾する方法が推奨されます。
フィルター パイプラインでのミドルウェアの使用
リソースフィルターは、パイプライン内の後に続くすべての処理を囲むように、ミドルウェアのように機能します。 ただし、フィルターはランタイムの一部である点がミドルウェアとは異なります。つまり、それらはコンテキストとコンストラクトにアクセスすることができます。
ミドルウェアをフィルターとして使用するには、フィルター パイプラインに挿入するミドルウェアを指定する Configure メソッドを使用して型を作成します。 ローカリゼーション ミドルウェアを使用して要求の現在のカルチャを確立する例を次に示します。
public class LocalizationPipeline
{
public void Configure(IApplicationBuilder applicationBuilder)
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("fr")
};
var options = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(
culture: "en-US",
uiCulture: "en-US"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
options.RequestCultureProviders = new[]
{ new RouteDataRequestCultureProvider() {
Options = options } };
applicationBuilder.UseRequestLocalization(options);
}
}
MiddlewareFilterAttribute を使用し、ミドルウェアを実行します。
[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
return Content(
$"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
+ $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}
ミドルウェア フィルターは、モデル バインドの前とパイプラインの残りの部分の後で、リソース フィルターと同じ段階でフィルター パイプラインで実行されます。
スレッドの安全性
その の代わりにフィルターの "Add" を Type に渡すとき、フィルターはシングルトンであり、スレッドセーフではありません。
次のアクション
- Razor Pages のフィルター メソッドに関するページをご覧ください。
- フィルターを試すには、GitHub のサンプルをダウンロードして、テストおよび変更を行います。
ASP.NET Core