ASP.NET Core フィルター

作成者: Kirk LarkinRick AndersonTom DykstraSteve Smith

ASP.NET Core でフィルターを使用すると、要求処理パイプラインの特定のステージの前または後にコードを実行できます。

組み込みのフィルターでは次のようなタスクが処理されます。

  • 許可。ユーザーに許可が与えられていないリソースの場合、アクセスを禁止する。
  • 応答キャッシュ。要求パイプラインを迂回し、キャッシュされている応答を返す。

横断的な問題を処理するカスタム フィルターを作成できます。 横断的な問題の例には、エラー処理、キャッシュ、構成、認証、ログなどがあります。 フィルターにより、コードの重複が回避されます。 たとえば、エラー処理例外フィルターではエラー処理を統合できます。

このドキュメントは、Razor Pages、API コントローラー、ビューのあるコントローラーに当てはまります。 フィルターは、Razor コンポーネントでは直接機能しません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。

  • コンポーネントがページまたはビューに埋め込まれている。
  • ページまたはコントローラーとビューでフィルターが使用されている。

フィルターのしくみ

ASP.NET Core のアクション呼び出しパイプライン内で実行されるフィルターは、フィルター パイプラインと呼ばれることがあります。 フィルター パイプラインは、ASP.NET Core が実行するアクションを選択した後に実行します。

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

フィルター タイプ

フィルターの種類はそれぞれ、フィルター パイプラインの異なるステージで実行されます。

  • 承認フィルター:

    • 最初に実行されます。
    • ユーザーが要求に対して承認されているかどうかを判断します。
    • 要求が承認されていない場合、パイプラインをショートサーキットします。
  • リソース フィルター:

    • 承認後に実行されます。
    • OnResourceExecuting では、残りのフィルター パイプラインの前にコードが実行されます。 たとえば、OnResourceExecuting では、モデル バインディングの前にコードが実行されます。
    • OnResourceExecuted では、残りのパイプラインの完了後にコードが実行されます。
  • アクション フィルター:

    • アクション メソッドが呼び出される直前と直後に実行します。
    • アクションに渡される引数を変更できます。
    • アクションから返された結果を変更できます。
    • Razor Pages ではサポートされていません
  • エンドポイント フィルター:

    • アクション メソッドが呼び出される直前と直後に実行します。
    • アクションに渡される引数を変更できます。
    • アクションから返された結果を変更できます。
    • Razor Pages ではサポートされていません
    • ルートハンドラー ベースのエンドポイントとコントローラー アクションの両方で呼び出すことができます。
  • 例外フィルターでは、応答本文への書き込みが行われる前に発生する未処理の例外にグローバル ポリシーが適用されます。

  • 結果フィルター:

    • アクション結果の実行の直前と直後に実行します。
    • アクション メソッドが正常に実行された場合にのみ実行します。
    • ビューまたはフォーマッタ実行を取り囲む必要があるロジックに便利です。

フィルターの種類がフィルター パイプラインでどのように連携しているかを、次の図に示します。

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor ページでは、Razor ページ ハンドラーの前後に実行する Razor ページ フィルターもサポートされます。

実装

フィルターは、異なるインターフェイス定義を介して、同期と非同期の実装をサポートします。

同期フィルターは、そのパイプライン ステージの前後に実行します。 たとえば、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 Value
  • another-filter-header: Another Filter Value

フィルター インターフェイスのいくつかには対応する属性があり、カスタムの実装に基底クラスとして使用できます。

フィルター属性:

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 コード。
  • グローバル フィルターの after コード。

次の例は、同期アクション フィルターに対してフィルター メソッドが実行される順序を示しています。

シークエンス フィルターのスコープ フィルター メソッド
1 グローバル OnActionExecuting
2 コントローラー OnActionExecuting
3 アクション OnActionExecuting
4 アクション OnActionExecuted
5 コントローラー OnActionExecuted
6 グローバル OnActionExecuted

コントローラー レベルのフィルター

Controller から継承するすべてのコントローラーには、OnActionExecutingOnActionExecutionAsyncOnActionExecuted メソッドが含まれます。 これらのメソッドは、特定のアクションに対して実行されるフィルターをラップします。

  • OnActionExecuting は、すべてのアクション フィルターの前に実行します。
  • OnActionExecuted は、すべてのアクション フィルターの後に実行します。
  • OnActionExecutionAsync は、すべてのアクション フィルターの前に実行します。 アクションのフィルターの後に、next への呼び出し後のコードを実行します。

次の ControllerFiltersController クラスは、次のように動作します。

  • SampleActionFilterAttribute ([SampleActionFilter]) をコントローラーに適用します。
  • OnActionExecutingOnActionExecuted がオーバーライドされます。
[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.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することはできません。 順序については、次のセクションで説明します。

Razor Pages の場合、フィルター メソッドをオーバーライドして Razor ページにフィルターを実装する方法に関するページを参照してください。

既定の順序をオーバーライドする

IOrderedFilter を実装することで、実行の既定の順序をオーバーライドできます。 IOrderedFilter により、実行の順序を決定するために、スコープよりも優先される Order プロパティが公開されます。 より低い Order 値を持つフィルター:

  • より高い Order の値を持つフィルターのそれよりも前に before コードが実行されます。
  • より高い Order の値を持つフィルターのそれよりも後に after コードが実行されます。

コントローラー レベルのフィルターの例では、GlobalSampleActionFilter はグローバル スコープを持つため、コントローラー スコープを持つ SampleActionFilterAttribute の前に実行します。 SampleActionFilterAttribute を最初に実行するようにするには、次のように順序を int.MinValue に設定します。

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

グローバル フィルター GlobalSampleActionFilter を最初に実行するようにするには、次のように Orderint.MinValue に設定します。

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

キャンセルとショートサーキット

フィルター メソッドに提供される ResourceExecutingContext パラメーターで Result プロパティを設定することで、フィルター パイプラインをショートサーキットできます。 たとえば、次のリソース フィルターは、パイプラインの残りの部分が実行されるのを防止します。

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 はアクション フィルターであるため、最初に実行されます。
  • パイプラインの残りの部分は迂回されます。

そのため、Index アクションの場合、ResponseHeaderAttribute フィルターが実行されることはありません。 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 は、IFilterMetadata インスタンスを作成するために CreateInstance メソッドを公開します。 CreateInstance により、DI から指定の型が読み込まれます。

TypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute と似ていますが、その型は 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 のインターフェイスを実装します。
  • 実行により、ほとんどのフィルター パイプラインがラップされます。
  • 承認フィルターのみ、リソース フィルターの前に実行されます。

リソース フィルターは、ほとんどのパイプラインをショートサーキットする目的で役に立ちます。 たとえば、キャッシュ フィルターにより、キャッシュ ヒットがあるとき、残りのパイプラインを回避できます。

リソース フィルターの例:

アクション フィルター

アクション フィルターは Razor Pages に適用されません。 Razor Pages では IPageFilterIAsyncPageFilter がサポートされています。 詳細については、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 は、ControllerResult に加え、次のプロパティを提供します。

  • 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 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)}");
}

例外フィルター:

  • before イベントと after イベントが与えられません。
  • 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.Canceltrue に設定することで、アクションの結果と後続の結果フィルターの実行をショートサーキットできます。 ショートサーキットする場合は、空の応答が生成されないように応答オブジェクトに記述します。 IResultFilter.OnResultExecuting での例外のスロー:

  • アクション結果と後続フィルターの実行を回避します。
  • 結果は成功ではなく、失敗として処理されます。

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted メソッドが実行されたとき、おそらく応答は既にクライアントに送信されています。 応答が既にクライアントに送信されていた場合は、それを変更することはできません。

別のフィルターによってアクションの結果の実行がショートサーキットされた場合、ResultExecutedContext.Canceledtrue に設定されます。

アクションの結果または後続の結果フィルターが例外をスローした場合、ResultExecutedContext.Exception は null 以外の値に設定されます。 Exception を null に設定すると、例外を効果的に処理し、パイプラインの後方で例外が再スローされるのを防ぐことができます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。

IAsyncResultFilter の場合、ResultExecutionDelegateawait next を呼び出すと、後続のすべての結果フィルターとアクションの結果が実行されます。 ショートサーキットするには、ResultExecutingContext.Canceltrue に設定します。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 の実装が宣言されます。 これには、以下によって生成されるアクションの結果が含まれます。

  • ショートサーキットが行われる承認フィルターとリソース フィルター。
  • 例外フィルター。

たとえば、次のフィルターは常に実行され、コンテンツ ネゴシエーションが失敗した場合に "422 処理不可エンティティ" 状態コードを使ってアクションの結果 (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 へのキャストが試行されます。 そのキャストが成功した場合、呼び出される IFilterMetadata インスタンスを作成するために CreateInstance メソッドが呼び出されます。 これにより、アプリの起動時に正確なフィルター パイプラインを明示的に設定する必要がないため、柔軟なデザインが可能になります。

IFilterFactory.IsReusable:

  • ファクトリによって作成されたフィルター インスタンスは、それが作成された要求範囲の外で使用される可能性があることがファクトリによって示されています。
  • シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください

ASP.NET Core ランタイムで保証されないこと:

  • フィルターのインスタンスが 1 つ作成されます。
  • フィルターが後の時点で、DI コンテナーから再要求されることはありません。

警告

フィルターが明確ではない、フィルターがステートレスである、フィルターは複数の HTTP 要求で安全に使用できる場合にのみ、true を返すように IFilterFactory.IsReusable を構成します。 たとえば、IFilterFactory.IsReusabletrue が返される場合、範囲付きまたは一時的として登録されているフィルターを 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 は、IFilterMetadata インスタンスを作成するために CreateInstance メソッドを公開します。 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)}");
}

ミドルウェア フィルターは、フィルター パイプラインのリソース フィルターと同じステージ (モデル バインドの前、残りのパイプラインの後) で実行されます。

スレッド セーフ

その Type の代わりにフィルターの "インスタンス" を Add に渡すとき、フィルターはシングルトンであり、スレッドセーフではありません

その他のリソース

作成者: Kirk LarkinRick AndersonTom DykstraSteve Smith

ASP.NET Core でフィルターを使用すると、要求処理パイプラインの特定のステージの前または後にコードを実行できます。

組み込みのフィルターでは次のようなタスクが処理されます。

  • 許可。ユーザーに許可が与えられていないリソースの場合、アクセスを禁止する。
  • 応答キャッシュ。要求パイプラインを迂回し、キャッシュされている応答を返す。

横断的な問題を処理するカスタム フィルターを作成できます。 横断的な問題の例には、エラー処理、キャッシュ、構成、認証、ログなどがあります。 フィルターにより、コードの重複が回避されます。 たとえば、エラー処理例外フィルターではエラー処理を統合できます。

このドキュメントは、Razor Pages、API コントローラー、ビューのあるコントローラーに当てはまります。 フィルターは、Razor コンポーネントでは直接機能しません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。

  • コンポーネントがページまたはビューに埋め込まれている。
  • ページまたはコントローラーとビューでフィルターが使用されている。

フィルターのしくみ

ASP.NET Core のアクション呼び出しパイプライン内で実行されるフィルターは、フィルター パイプラインと呼ばれることがあります。 フィルター パイプラインは、ASP.NET Core が実行するアクションを選択した後に実行します。

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

フィルター タイプ

フィルターの種類はそれぞれ、フィルター パイプラインの異なるステージで実行されます。

  • 承認フィルター:

    • 最初に実行されます。
    • ユーザーが要求に対して承認されているかどうかを判断します。
    • 要求が承認されていない場合、パイプラインをショートサーキットします。
  • リソース フィルター:

    • 承認後に実行されます。
    • OnResourceExecuting では、残りのフィルター パイプラインの前にコードが実行されます。 たとえば、OnResourceExecuting では、モデル バインディングの前にコードが実行されます。
    • OnResourceExecuted では、残りのパイプラインの完了後にコードが実行されます。
  • アクション フィルター:

    • アクション メソッドが呼び出される直前と直後に実行します。
    • アクションに渡される引数を変更できます。
    • アクションから返された結果を変更できます。
    • Razor Pages ではサポートされていません
  • エンドポイント フィルター:

    • アクション メソッドが呼び出される直前と直後に実行します。
    • アクションに渡される引数を変更できます。
    • アクションから返された結果を変更できます。
    • Razor Pages ではサポートされていません
    • ルートハンドラー ベースのエンドポイントとコントローラー アクションの両方で呼び出すことができます。
  • 例外フィルターでは、応答本文への書き込みが行われる前に発生する未処理の例外にグローバル ポリシーが適用されます。

  • 結果フィルター:

    • アクション結果の実行の直前と直後に実行します。
    • アクション メソッドが正常に実行された場合にのみ実行します。
    • ビューまたはフォーマッタ実行を取り囲む必要があるロジックに便利です。

フィルターの種類がフィルター パイプラインでどのように連携しているかを、次の図に示します。

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor ページでは、Razor ページ ハンドラーの前後に実行する Razor ページ フィルターもサポートされます。

実装

フィルターは、異なるインターフェイス定義を介して、同期と非同期の実装をサポートします。

同期フィルターは、そのパイプライン ステージの前後に実行します。 たとえば、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 Value
  • another-filter-header: Another Filter Value

フィルター インターフェイスのいくつかには対応する属性があり、カスタムの実装に基底クラスとして使用できます。

フィルター属性:

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 コード。
  • グローバル フィルターの after コード。

次の例は、同期アクション フィルターに対してフィルター メソッドが実行される順序を示しています。

シークエンス フィルターのスコープ フィルター メソッド
1 グローバル OnActionExecuting
2 コントローラー OnActionExecuting
3 アクション OnActionExecuting
4 アクション OnActionExecuted
5 コントローラー OnActionExecuted
6 グローバル OnActionExecuted

コントローラー レベルのフィルター

Controller から継承するすべてのコントローラーには、OnActionExecutingOnActionExecutionAsyncOnActionExecuted メソッドが含まれます。 これらのメソッドは、特定のアクションに対して実行されるフィルターをラップします。

  • OnActionExecuting は、すべてのアクション フィルターの前に実行します。
  • OnActionExecuted は、すべてのアクション フィルターの後に実行します。
  • OnActionExecutionAsync は、すべてのアクション フィルターの前に実行します。 アクションのフィルターの後に、next への呼び出し後のコードを実行します。

次の ControllerFiltersController クラスは、次のように動作します。

  • SampleActionFilterAttribute ([SampleActionFilter]) をコントローラーに適用します。
  • OnActionExecutingOnActionExecuted がオーバーライドされます。
[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.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することはできません。 順序については、次のセクションで説明します。

Razor Pages の場合、フィルター メソッドをオーバーライドして Razor ページにフィルターを実装する方法に関するページを参照してください。

既定の順序をオーバーライドする

IOrderedFilter を実装することで、実行の既定の順序をオーバーライドできます。 IOrderedFilter により、実行の順序を決定するために、スコープよりも優先される Order プロパティが公開されます。 より低い Order 値を持つフィルター:

  • より高い Order の値を持つフィルターのそれよりも前に before コードが実行されます。
  • より高い Order の値を持つフィルターのそれよりも後に after コードが実行されます。

コントローラー レベルのフィルターの例では、GlobalSampleActionFilter はグローバル スコープを持つため、コントローラー スコープを持つ SampleActionFilterAttribute の前に実行します。 SampleActionFilterAttribute を最初に実行するようにするには、次のように順序を int.MinValue に設定します。

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

グローバル フィルター GlobalSampleActionFilter を最初に実行するようにするには、次のように Orderint.MinValue に設定します。

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

キャンセルとショートサーキット

フィルター メソッドに提供される ResourceExecutingContext パラメーターで Result プロパティを設定することで、フィルター パイプラインをショートサーキットできます。 たとえば、次のリソース フィルターは、パイプラインの残りの部分が実行されるのを防止します。

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 はアクション フィルターであるため、最初に実行されます。
  • パイプラインの残りの部分は迂回されます。

そのため、Index アクションの場合、ResponseHeaderAttribute フィルターが実行されることはありません。 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 は、IFilterMetadata インスタンスを作成するために CreateInstance メソッドを公開します。 CreateInstance により、DI から指定の型が読み込まれます。

TypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute と似ていますが、その型は 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 のインターフェイスを実装します。
  • 実行により、ほとんどのフィルター パイプラインがラップされます。
  • 承認フィルターのみ、リソース フィルターの前に実行されます。

リソース フィルターは、ほとんどのパイプラインをショートサーキットする目的で役に立ちます。 たとえば、キャッシュ フィルターにより、キャッシュ ヒットがあるとき、残りのパイプラインを回避できます。

リソース フィルターの例:

アクション フィルター

アクション フィルターは Razor Pages に適用されません。 Razor Pages では IPageFilterIAsyncPageFilter がサポートされています。 詳細については、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 は、ControllerResult に加え、次のプロパティを提供します。

  • 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 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)}");
}

例外フィルター:

  • before イベントと after イベントが与えられません。
  • 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.Canceltrue に設定することで、アクションの結果と後続の結果フィルターの実行をショートサーキットできます。 ショートサーキットする場合は、空の応答が生成されないように応答オブジェクトに記述します。 IResultFilter.OnResultExecuting での例外のスロー:

  • アクション結果と後続フィルターの実行を回避します。
  • 結果は成功ではなく、失敗として処理されます。

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted メソッドが実行されたとき、おそらく応答は既にクライアントに送信されています。 応答が既にクライアントに送信されていた場合は、それを変更することはできません。

別のフィルターによってアクションの結果の実行がショートサーキットされた場合、ResultExecutedContext.Canceledtrue に設定されます。

アクションの結果または後続の結果フィルターが例外をスローした場合、ResultExecutedContext.Exception は null 以外の値に設定されます。 Exception を null に設定すると、例外を効果的に処理し、パイプラインの後方で例外が再スローされるのを防ぐことができます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。

IAsyncResultFilter の場合、ResultExecutionDelegateawait next を呼び出すと、後続のすべての結果フィルターとアクションの結果が実行されます。 ショートサーキットするには、ResultExecutingContext.Canceltrue に設定します。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 の実装が宣言されます。 これには、以下によって生成されるアクションの結果が含まれます。

  • ショートサーキットが行われる承認フィルターとリソース フィルター。
  • 例外フィルター。

たとえば、次のフィルターは常に実行され、コンテンツ ネゴシエーションが失敗した場合に "422 処理不可エンティティ" 状態コードを使ってアクションの結果 (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 へのキャストが試行されます。 そのキャストが成功した場合、呼び出される IFilterMetadata インスタンスを作成するために CreateInstance メソッドが呼び出されます。 これにより、アプリの起動時に正確なフィルター パイプラインを明示的に設定する必要がないため、柔軟なデザインが可能になります。

IFilterFactory.IsReusable:

  • ファクトリによって作成されたフィルター インスタンスは、それが作成された要求範囲の外で使用される可能性があることがファクトリによって示されています。
  • シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください

ASP.NET Core ランタイムで保証されないこと:

  • フィルターのインスタンスが 1 つ作成されます。
  • フィルターが後の時点で、DI コンテナーから再要求されることはありません。

警告

フィルターが明確ではない、フィルターがステートレスである、フィルターは複数の HTTP 要求で安全に使用できる場合にのみ、true を返すように IFilterFactory.IsReusable を構成します。 たとえば、IFilterFactory.IsReusabletrue が返される場合、範囲付きまたは一時的として登録されているフィルターを 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 は、IFilterMetadata インスタンスを作成するために CreateInstance メソッドを公開します。 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)}");
}

ミドルウェア フィルターは、フィルター パイプラインのリソース フィルターと同じステージ (モデル バインドの前、残りのパイプラインの後) で実行されます。

スレッド セーフ

その Type の代わりにフィルターの "インスタンス" を Add に渡すとき、フィルターはシングルトンであり、スレッドセーフではありません

その他のリソース

作成者: Kirk LarkinRick AndersonTom DykstraSteve Smith

ASP.NET Core でフィルターを使用すると、要求処理パイプラインの特定のステージの前または後にコードを実行できます。

組み込みのフィルターでは次のようなタスクが処理されます。

  • 許可。ユーザーに許可が与えられていないリソースの場合、アクセスを禁止する。
  • 応答キャッシュ。要求パイプラインを迂回し、キャッシュされている応答を返す。

横断的な問題を処理するカスタム フィルターを作成できます。 横断的な問題の例には、エラー処理、キャッシュ、構成、認証、ログなどがあります。 フィルターにより、コードの重複が回避されます。 たとえば、エラー処理例外フィルターではエラー処理を統合できます。

このドキュメントは、Razor Pages、API コントローラー、ビューのあるコントローラーに当てはまります。 フィルターは、Razor コンポーネントでは直接機能しません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。

  • コンポーネントがページまたはビューに埋め込まれている。
  • ページまたはコントローラーとビューでフィルターが使用されている。

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

フィルターのしくみ

ASP.NET Core のアクション呼び出しパイプライン内で実行されるフィルターは、フィルター パイプラインと呼ばれることがあります。 フィルター パイプラインは、ASP.NET Core が実行するアクションを選択した後に実行されます。

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

フィルター タイプ

フィルターの種類はそれぞれ、フィルター パイプラインの異なるステージで実行されます。

  • 承認フィルターは、最初に実行され、ユーザーが要求に対して承認されているかどうかを判断するために使用されます。 承認フィルターでは、要求が承認されていない場合、パイプラインがショートサーキットされます。

  • リソース フィルター:

    • 承認後に実行されます。
    • OnResourceExecuting では、残りのフィルター パイプラインの前にコードが実行されます。 たとえば、OnResourceExecuting では、モデル バインディングの前にコードが実行されます。
    • OnResourceExecuted では、残りのパイプラインの完了後にコードが実行されます。
  • アクション フィルター:

    • アクション メソッドが呼び出される直前と直後にコードを実行します。
    • アクションに渡される引数を変更できます。
    • アクションから返された結果を変更できます。
    • Razor Pages ではサポートされていません
  • 例外フィルターでは、応答本文への書き込みが行われる前に発生する未処理の例外にグローバル ポリシーが適用されます。

  • 結果フィルターでは、アクション結果の実行の直前と直後にコードが実行されます。 アクション メソッドが正常に実行された場合にのみ実行されます。 ビューまたはフォーマッタ実行を取り囲む必要があるロジックに便利です。

フィルターの種類がフィルター パイプラインでどのように連携しているかを、次の図に示します。

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

実装

フィルターは、異なるインターフェイス定義を介して、同期と非同期の実装をサポートします。

同期フィルターでは、そのパイプライン ステージの前と後にコードが実行されます。 たとえば、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 では、次のことが行われます。

  • "Position" 構成領域を使用して PositionOptions クラスをサービス コンテナーに追加します。
  • 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; }
}

次のコードでは、Index2 メソッドに MyActionFilterAttribute が適用されています。

[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.");
    }

Sample/Index2 エンドポイントが呼び出されると、[応答ヘッダー] の下に author: Rick Anderson および Editor: Joe Smith が表示されます。

次のコードでは、MyActionFilterAttributeAddHeaderAttribute が Razor ページに適用されます。

[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Razor ページ ハンドラー メソッドにフィルターを適用することはできません。 これらは、Razor ページ モデルに適用するか、グローバルに適用することが可能です。

フィルター インターフェイスのいくつかには対応する属性があり、カスタムの実装に基底クラスとして使用できます。

フィルター属性:

フィルターのスコープと実行の順序

フィルターは、3 つのスコープのいずれかでパイプラインに追加することができます。

  • コントローラー アクションでの属性の使用。 フィルター属性を Razor Pages ハンドラー メソッドに適用することはできません。
  • コントローラーまたは Razor ページでの属性の使用。
  • 次のコードのように、すべてのコントローラー、アクション、および Razor Pages に対してグローバルに:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

実行の既定の順序

パイプラインの特定のステージに対して複数のフィルターがある場合に、スコープがフィルターの実行の既定の順序を決定します。 グローバル フィルターがクラス フィルターを囲み、クラス フィルターがメソッド フィルターを囲みます。

フィルターの入れ子の結果として、フィルターの after コードが before コードと逆の順序で実行されます。 フィルター シーケンス:

  • グローバル フィルターの before コード。
    • コントローラーおよび Razor ページ フィルターの before コード。
      • アクション メソッド フィルターの before コード。
      • アクション メソッド フィルターの after コード。
    • コントローラーおよび Razor ページ フィルターの after コード。
  • グローバル フィルターの after コード。

次の例は、同期アクション フィルターに対してフィルター メソッドが呼び出される順序を示しています。

シークエンス フィルターのスコープ フィルター メソッド
1 グローバル OnActionExecuting
2 コントローラーまたは Razor ページ OnActionExecuting
3 方法 OnActionExecuting
4 方法 OnActionExecuted
5 コントローラーまたは Razor ページ OnActionExecuted
6 グローバル OnActionExecuted

コントローラー レベルのフィルター

Controller 基底クラスから継承するすべてのコントローラーには、Controller.OnActionExecutingController.OnActionExecutionAsyncController.OnActionExecutedOnActionExecuted メソッドが含まれます。 これらのメソッド:

  • 特定のアクションに対して実行されるフィルターをラップします。
  • OnActionExecuting は、あらゆるアクション フィルターの前に呼び出されます。
  • OnActionExecuted は、あらゆるアクション フィルターの後に呼び出されます。
  • OnActionExecutionAsync は、あらゆるアクション フィルターの前に呼び出されます。 next の後のフィルターのコードは、アクション メソッドの後に実行されます。

たとえば、ダウンロード サンプルで、MySampleActionFilter は起動中、グローバルに適用されます。

TestController:

  • SampleActionFilterAttribute ([SampleActionFilter]) が FilterTest2 アクションに適用されます。
  • OnActionExecutingOnActionExecuted がオーバーライドされます。
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);
    }
}

MyDisplayRouteInfoRick.Docs.Samples.RouteInfo NuGet パッケージによって提供され、ルート情報が表示されます。

https://localhost:5001/Test/FilterTest2 に移動すると、次のコードが実行されます。

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

コントローラー レベルのフィルターでは、Order プロパティが int.MinValue に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することはできません。 順序については、次のセクションで説明します。

Razor Pages の場合、フィルター メソッドをオーバーライドして Razor ページにフィルターを実装する方法に関するページを参照してください。

既定の順序のオーバーライド

IOrderedFilter を実装することで、実行の既定の順序をオーバーライドできます。 IOrderedFilter により、実行の順序を決定するために、スコープよりも優先される Order プロパティが公開されます。 より低い Order 値を持つフィルター:

  • より高い Order の値を持つフィルターのそれよりも前に before コードが実行されます。
  • より高い Order の値を持つフィルターのそれよりも後に after コードが実行されます。

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.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • MyAction2FilterAttribute.OnActionExecuting
        • Test2Controller.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 を最初に実行するようにするには、次のように Orderint.MinValue に設定します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter),
                            int.MinValue);
    });
}

キャンセルとショートサーキット

フィルター メソッドに提供される ResourceExecutingContext パラメーターで Result プロパティを設定することで、フィルター パイプラインをショートサーキットできます。 たとえば、次のリソース フィルターは、パイプラインの残りの部分が実行されるのを防止します。

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)
    {
    }
}

次のコードでは、ShortCircuitingResourceFilterAddHeader の両方のフィルターが SomeResource アクション メソッドをターゲットにしています。 ShortCircuitingResourceFilter:

  • これはリソース フィルターであり、AddHeader はアクション フィルターであるため、最初に実行されます。
  • パイプラインの残りの部分は迂回されます。

そのため、SomeResource アクションの場合、AddHeader フィルターが実行されることはありません。 ShortCircuitingResourceFilter が最初に実行された場合は、両方のフィルターがアクション メソッド レベルで適用されると、この動作が同じになります。 そのフィルターの種類が原因で、あるいは Order プロパティの明示的な使用により、ShortCircuitingResourceFilter が最初に実行されます。

[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 は、IFilterMetadata インスタンスを作成するために CreateInstance メソッドを公開します。 CreateInstance により、DI から指定の型が読み込まれます。

TypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute と似ていますが、その型は 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 のインターフェイスを実装します。
  • 実行により、ほとんどのフィルター パイプラインがラップされます。
  • 承認フィルターのみ、リソース フィルターの前に実行されます。

リソース フィルターは、ほとんどのパイプラインをショートサーキットする目的で役に立ちます。 たとえば、キャッシュ フィルターにより、キャッシュ ヒットがあるとき、残りのパイプラインを回避できます。

リソース フィルターの例:

アクション フィルター

アクション フィルターは Razor Pages に適用されません。 Razor Pages では IPageFilterIAsyncPageFilter がサポートされています。 詳細については、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 は、ControllerResult に加え、次のプロパティを提供します。

  • 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);
    }
}

例外フィルター

例外フィルター:

次の例外フィルターのサンプルでは、カスタムのエラー ビューを使用して、アプリの開発中に発生する例外に関する詳細を表示します。

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.");
    }
}

例外フィルター:

  • before イベントと after イベントが与えられません。
  • 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.Canceltrue に設定することで、アクションの結果と後続の結果フィルターの実行をショートサーキットできます。 ショートサーキットする場合は、空の応答が生成されないように応答オブジェクトに記述します。 IResultFilter.OnResultExecuting での例外のスロー:

  • アクション結果と後続フィルターの実行を回避します。
  • 結果は成功ではなく、失敗として処理されます。

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted メソッドが実行されたとき、おそらく応答は既にクライアントに送信されています。 応答が既にクライアントに送信されていた場合は、それを変更することはできません。

別のフィルターによってアクションの結果の実行がショートサーキットされた場合、ResultExecutedContext.Canceledtrue に設定されます。

アクションの結果または後続の結果フィルターが例外をスローした場合、ResultExecutedContext.Exception は null 以外の値に設定されます。 Exception を null に設定すると、例外を効果的に処理し、パイプラインの後方で例外が再スローされるのを防ぐことができます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。

IAsyncResultFilter の場合、ResultExecutionDelegateawait next を呼び出すと、後続のすべての結果フィルターとアクションの結果が実行されます。 ショートサーキットするには、ResultExecutingContext.Canceltrue に設定します。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 の実装が宣言されます。 これには、以下によって生成されるアクションの結果が含まれます。

  • ショートサーキットが行われる承認フィルターとリソース フィルター。
  • 例外フィルター。

たとえば、次のフィルターは常に実行され、コンテンツ ネゴシエーションが失敗した場合に "422 処理不可エンティティ" 状態コードを使ってアクションの結果 (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 へのキャストが試行されます。 そのキャストが成功した場合、呼び出される IFilterMetadata インスタンスを作成するために CreateInstance メソッドが呼び出されます。 これにより、アプリの起動時に正確なフィルター パイプラインを明示的に設定する必要がないため、柔軟なデザインが可能になります。

IFilterFactory.IsReusable:

  • ファクトリによって作成されたフィルター インスタンスは、それが作成された要求範囲の外で使用される可能性があることがファクトリによって示されています。
  • シングルトン以外で有効期間があるサービスに依存するフィルターと共に使用しないでください

ASP.NET Core ランタイムで保証されないこと:

  • フィルターのインスタンスが 1 つ作成されます。
  • フィルターが後の時点で、DI コンテナーから再要求されることはありません。

警告

フィルターが明確ではない、フィルターがステートレスである、フィルターは複数の HTTP 要求で安全に使用できる場合にのみ、true を返すように IFilterFactory.IsReusable を構成します。 たとえば、IFilterFactory.IsReusabletrue が返される場合、範囲付きまたは一時的として登録されているフィルターを 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 開発者ツールでは、サンプル コードによって追加された次の応答ヘッダーが表示されます。

  • author:Rick Anderson
  • globaladdheader:Result filter added to MvcOptions.Filters
  • internal:My header

上記のコードでは、internal:My header という応答ヘッダーが作成されます。

属性に実装された IFilterFactory

IFilterFactory を実装するフィルターは次のようなフィルターに便利です。

  • パラメーターの引き渡しを必要としません。
  • DI で満たす必要があるコンストラクター依存関係があります。

TypeFilterAttribute は、IFilterFactory を実装します。 IFilterFactory は、IFilterMetadata インスタンスを作成するために CreateInstance メソッドを公開します。 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}");
}

ミドルウェア フィルターは、フィルター パイプラインのリソース フィルターと同じステージ (モデル バインドの前、残りのパイプラインの後) で実行されます。

スレッド セーフ

その Type の代わりにフィルターの "インスタンス" を Add に渡すとき、フィルターはシングルトンであり、スレッドセーフではありません

次の操作