ASP.NET Core フィルター

作成者: Kirk LarkinRick AndersonTom DykstraSteve Smith

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

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

  • 承認。ユーザーが承認されていないリソースへのアクセスを防止します。
  • 応答のキャッシュ。要求パイプラインをショートサーキットして、キャッシュされた応答を返します。

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

このドキュメントは、 Razor ビューがあるページ、API コントローラー、およびコントローラーに適用されます。 フィルターは、 Razor コンポーネントで直接使用することはできません。 次の場合、フィルターは間接的にコンポーネントに影響するのみです。

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

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

フィルターのしくみ

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

要求は、他のミドルウェア、ルーティング ミドルウェア、アクション選択、およびアクション呼び出しパイプラインを通じて処理されます。 要求の処理は、クライアントに送信される応答になる前に、アクション選択、ルーティング ミドルウェア、およびさまざまな他のミドルウェアを遡って続行されます。

フィルターの種類

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

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

  • リソースフィルター:

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

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

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

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

要求は、承認フィルター、リソース フィルター、モデル バインド、アクション フィルター、アクションの実行とアクション結果の変換、例外フィルター、結果フィルター、結果の実行を介して処理されます。 終了間際に、要求は結果フィルターとリソース フィルターのみで処理されてから、クライアントに送信される応答になります。

実装

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

同期フィルターでは、そのパイプライン ステージの前と後にコードが実行されます。 たとえば、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": "エディター",
        "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.");
    }

応答ヘッダー では、 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 ます。

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

フィルター属性:

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

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

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

実行の既定の順序

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

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

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

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

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

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

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

  • 特定のアクションに対して実行されるフィルターをラップします。
  • 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 に設定されます。 コントローラー レベルのフィルターは、メソッドにフィルターが適用された後に実行されるように設定することは できません。 順序については、次のセクションで説明します。

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

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

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

アクション フィルター

アクション フィルターは Pages には適用 Razor されません。 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 は、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);
        }
    }

注意

属性で注釈が付されたコントローラー [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 例外を処理します。
  • リソース フィルター、結果フィルター、または MVC 結果の実行で発生した例外はキャッチ しません

例外を処理するには、ExceptionHandled プロパティを true に設定するか、応答を記述します。 これにより、例外の伝達を停止します。 例外フィルターで例外を "成功" に変えることはできません。 それができるのは、アクション フィルターだけです。

例外フィルター:

  • アクション内で発生する例外のトラップに適しています。
  • エラー処理ミドルウェアほど柔軟ではありません。

例外処理にはミドルウェアを選択してください。 呼び出されたアクション メソッドに基づいてエラー処理が 異なる 状況でのみ例外フィルターを使用します。 たとえば、アプリには、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 要求で安全に使用できる場合にのみ、 を返す IFilterFactory.IsReusable true 構成を行います。 たとえば、 が を返す場合は、スコープ付きとして登録されている DI からフィルターを返したり、一時的なフィルターを IFilterFactory.IsReusable 返したりしない場合があります true

フィルターを作成するための別の方法として、カスタムの属性の実装で 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}");
}

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

スレッド セーフ

フィルターのインスタンス ではなく に渡す場合、フィルターはシングルトンであり、 Add Type スレッド セーフ ではありません。

次の操作

作成者: Kirk LarkinRick AndersonTom DykstraSteve Smith

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

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

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

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

このドキュメントは Razor 、Pages、API コントローラー、およびビューを持つコントローラーに適用されます。

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

フィルターのしくみ

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

要求は、他のミドルウェア、ルーティング ミドルウェア、アクション選択、ASP.NET Core のアクション呼び出しパイプラインを通じて処理されます。 要求の処理は、クライアントに送信される応答になる前に、アクション選択、ルーティング ミドルウェア、およびさまざまな他のミドルウェアを遡って続行されます。

フィルターの種類

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

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

  • リソース フィルター:

    • 承認後に実行されます。
    • OnResourceExecuting では、残りのフィルター パイプラインの前にコードを実行できます。 たとえば、OnResourceExecuting では、モデル バインディングの前にコードを実行できます。
    • OnResourceExecuted では、残りのパイプラインの完了後にコードを実行できます。
  • アクション フィルターは、個々のアクション メソッドが呼び出される直前と直後にコードを実行できます。 アクションに渡される引数とアクションから返される結果を操作するために使用できます。 アクション フィルターは Pages ではサポート Razor されていません。

  • 例外フィルターは、応答本文に何かが書き込まれる前に発生する未処理の例外にグローバル ポリシーを適用するために使用されます。

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

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

要求は、承認フィルター、リソース フィルター、モデル バインド、アクション フィルター、アクションの実行とアクション結果の変換、例外フィルター、結果フィルター、結果の実行を介して処理されます。 終了間際に、要求は結果フィルターとリソース フィルターのみで処理されてから、クライアントに送信される応答になります。

実装

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

同期フィルターでは、パイプライン ステージの前 (On-Stage-Executing) と後 (On-Stage-Executed) にコードを実行できます。 たとえば、OnActionExecuting はアクション メソッドの呼び出し前に呼び出されます。 OnActionExecuted は、アクション メソッドが戻った後に呼び出されます。

public class MySampleActionFilter : 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 メソッドが定義されます。

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) があります。 各 On-Stage-ExecutionAsync メソッドは、フィルターのパイプライン ステージを実行する FilterType-ExecutionDelegate を受け取ります。

複数のフィルター ステージ

複数のフィルター ステージのためのインターフェイスを 1 つのクラスで実装できます。 たとえば、ActionFilterAttribute クラスは、IActionFilterIResultFilter、およびそれらの非同期バージョンを実装します。

フィルター インターフェイスの同期と非同期バージョンの 両方ではなくいずれか を実装します。 ランタイムは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。 していない場合は、同期インターフェイスのメソッドを呼び出します。 非同期インターフェイスと同期インターフェイスの両方が 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", "Joe Smith")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

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

フィルター属性:

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

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

  • アクションで属性を使用します。
  • コントローラーで属性を使用します。
  • 次のコードのように、すべてのコントローラーとアクションに対してグローバルに:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(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
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

上記のコードでは、MvcOptions.Filters コレクションを利用し、3 つのフィルターがグローバルに追加されます。

実行の既定の順序

"同じ種類のフィルター" が複数存在する場合は、スコープによって、フィルターの実行の既定の順序が決まります。 グローバル フィルターによってクラス フィルターが囲まれます。 クラス フィルターによってメソッド フィルターが囲まれます。

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

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

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

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

このシーケンスが示すもの:

  • メソッド フィルターは、コントローラー フィルター内で入れ子になります。
  • コントローラー フィルターは、グローバル フィルター内で入れ子になります。

コントローラーおよび Razor ページレベルのフィルター

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

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

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

TestController:

  • SampleActionFilterAttribute ([SampleActionFilter]) が FilterTest2 アクションに適用されます。
  • OnActionExecutingOnActionExecuted がオーバーライドされます。
public class TestController : Controller
{
    [SampleActionFilter]
    public IActionResult FilterTest2()
    {
        return Content($"From FilterTest2");
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        base.OnActionExecuted(context);
    }
}

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

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

ページについて Razor は、「 Razor フィルターメソッドをオーバーライドしてページフィルターを実装する」を参照してください。

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

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

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

Order プロパティはコンストラクター パラメーターで設定できます。

[MyFilter(Name = "Controller Level Attribute", Order=1)]

上記の例にある同じ 3 つのアクション フィルターを検討してください。 コントローラーとグローバル フィルターの Order プロパティが 1 と 2 にそれぞれ設定される場合、実行順序が逆になります。

シーケンス フィルターのスコープ Order プロパティ フィルター メソッド
1 メソッド 0 OnActionExecuting
2 コントローラー 1 OnActionExecuting
3 グローバル 2 OnActionExecuting
4 グローバル 2 OnActionExecuted
5 コントローラー 1 OnActionExecuted
6 メソッド 0 OnActionExecuted

フィルターの実行順序を決定するときに、Order プロパティによりスコープがオーバーライドされます。 最初に順序でフィルターが並べ替えられ、次に同じ順位の優先度を決めるためにスコープが使用されます。 組み込みのフィルターはすべて IOrderedFilter を実装し、既定の Order 値を 0 に設定します。 組み込みのフィルターの場合、Order をゼロ以外の値に設定しない限り、スコープによって順序が決定されます。

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

フィルター メソッドに提供される 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", "Joe Smith")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

依存関係の挿入

フィルターは種類ごとまたはインスタンスごとに追加できます。 インスタンスが追加される場合、そのインスタンスはすべての要求に対して使用されます。 種類が追加される場合、それは種類でアクティブ化されます。 種類でアクティブ化されるフィルターの意味:

  • インスタンスは要求ごとに作成されます。
  • コンストラクターの依存関係は、依存関係の挿入 (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.
    }
}

次のコードでは、AddHeaderResultServiceFilter が DI コンテナーに追加されます。

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddMvc(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
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

次のコードでは、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}");
}
public class LogConstantFilter : IActionFilter
{
    private readonly string _value;
    private readonly ILogger<LogConstantFilter> _logger;

    public LogConstantFilter(string value, ILogger<LogConstantFilter> logger)
    {
        _logger = logger;
        _value = value;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation(_value);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    { }
}

承認フィルター

承認フィルター:

  • フィルター パイプライン内で実行される最初のフィルターです。
  • アクション メソッドへのアクセスを制御します。
  • before メソッドが与えられ、after メソッドは与えられません。

カスタム承認フィルターには、カスタム承認フレームワークが必要です。 カスタム フィルターを記述するよりも、独自の承認ポリシーを構成するか、カスタム承認ポリシーを記述することを選びます。 組み込み承認フィルター:

  • 承認システムを呼び出します。
  • 要求は承認されません。

承認フィルター内で例外をスロー しません

  • 例外は処理されません。
  • 例外フィルターで例外が処理されません。

承認フィルターで例外が発生した場合、チャレンジ発行を検討してください。

承認の詳細については、こちらを参照してください。

リソース フィルター

リソース フィルター:

  • IResourceFilter または IAsyncResourceFilter のインターフェイスを実装します。
  • 実行により、ほとんどのフィルター パイプラインがラップされます。
  • 承認フィルターのみ、リソース フィルターの前に実行されます。

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

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

アクション フィルター

重要

アクションフィルターは、ページには適用 されません Razor 。 Razor ページは IPageFilter とをサポートし IAsyncPageFilter ます。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。

アクション フィルター:

  • IActionFilter または IAsyncActionFilter のインターフェイスを実装します。
  • この実行はアクション メソッドの実行を取り囲みます。

次のコードは、サンプル アクション フィルターを示しています。

public class MySampleActionFilter : 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);
        }
    }

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 IHostingEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilter(
        IHostingEnvironment 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;
    }
}

例外フィルター:

  • before イベントと after イベントが与えられません。
  • OnException または OnExceptionAsync を実装します。
  • Razorページまたはコントローラーの作成、モデルのバインド、アクションフィルター、アクションメソッドで発生する未処理の例外を処理します。
  • リソース フィルター、結果フィルター、または MVC 結果の実行で発生した例外はキャッチ しません

例外を処理するには、ExceptionHandled プロパティを true に設定するか、応答を記述します。 これにより、例外の伝達を停止します。 例外フィルターで例外を "成功" に変えることはできません。 それができるのは、アクション フィルターだけです。

例外フィルター:

  • アクション内で発生する例外のトラップに適しています。
  • エラー処理ミドルウェアほど柔軟ではありません。

例外処理にはミドルウェアを選択してください。 呼び出されたアクション メソッドに基づいてエラー処理が 異なる 状況でのみ例外フィルターを使用します。 たとえば、アプリには、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.
    }
}

実行されている結果の種類は、アクションに依存します。 ビューを返すアクションには、実行されている 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 に設定すると、例外を効果的に処理でき、パイプラインの後方で ASP.NET Core によって例外が再スローされることを防げます。 結果フィルターの例外を処理するとき、応答にデータを書き込む目的で信頼できる方法はありません。 アクションの結果により例外がスローされるとき、ヘッダーがクライアントにフラッシュされている場合、エラー コードを送信する目的で信頼できるメカニズムはありません。

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 を実装できます。

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

ダウンロード サンプルを実行することで、前のコードをテストできます。

  • F12 開発者ツールを呼び出します。
  • https://localhost:5001/Sample/HeaderWithFactory に移動します。

F12 開発者ツールでは、サンプル コードによって追加された次の応答ヘッダーが表示されます。

  • 作成者:Joe Smith
  • globaladdheader: Result filter added to MvcOptions.Filters
  • 内部: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("Business action starting...");
            // perform some business logic work

        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // perform some business logic work
            _logger.LogInformation("Business action completed.");
        }
    }
}

次のコードでは、[SampleActionFilter] を適用する 3 つの手法を確認できます。

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content($"From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content($"From ServiceFilterTest");
}

// 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] でメソッドを装飾する方法が推奨されます。

フィルター パイプラインでのミドルウェアの使用

リソース フィルターは、パイプラインの後方で登場するすべての実行を囲む点で、ミドルウェアのように機能します。 ただし、フィルターは ASP.NET Core ランタイムの一部である点がミドルウェアとは異なります。つまり、フィルターには ASP.NET Core コンテキストとコンストラクトへのアクセスがあります。

ミドルウェアをフィルターとして使用するには、フィルター パイプラインに挿入するミドルウェアを指定する 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 フィルターはシングルトンになり、スレッドセーフでは ありません

次の操作