ASP.NET Core の Razor Pages のフィルター メソッドFilter methods for Razor Pages in ASP.NET Core

作成者: Rick AndersonBy Rick Anderson

Razor ページのフィルターである IPageFilter および IAsyncPageFilter を使用すると、Razor ページ ハンドラーの実行の前後に Razor Pages でコードを実行できます。Razor Page filters IPageFilter and IAsyncPageFilter allow Razor Pages to run code before and after a Razor Page handler is run. Razor ページ フィルターは、個々のページ ハンドラー メソッドに適用できないことを除き、ASP.NET Core MVC アクション フィルターと類似しています。Razor Page filters are similar to ASP.NET Core MVC action filters, except they can't be applied to individual page handler methods.

Razor ページ フィルターRazor Page filters:

  • モデルのバインドが行われる前の、ハンドラー メソッドが選択された後にコードを実行します。Run code after a handler method has been selected, but before model binding occurs.
  • モデルのバインドの完了後の、ハンドラー メソッドの実行前にコードを実行します。Run code before the handler method executes, after model binding is complete.
  • ハンドラー メソッドの実行後にコードを実行します。Run code after the handler method executes.
  • ページまたはグローバルに実装できます。Can be implemented on a page or globally.
  • 特定のページ ハンドラー メソッドには適用できません。Cannot be applied to specific page handler methods.
  • コンストラクターの依存関係は、依存関係の挿入 (DI) によって入力されるようにできます。Can have constructor dependencies populated by Dependency Injection (DI). 詳細については、「ServiceFilterAttribute」と「TypeFilterAttribute」を参照してください。For more information, see ServiceFilterAttribute and TypeFilterAttribute.

ページ コンストラクターとミドルウェアにより、ハンドラー メソッドが実行される前にカスタム コードの実行が可能になりますが、HttpContext とページへのアクセスを可能にするのは Razor ページ フィルターのみです。While page constructors and middleware enable executing custom code before a handler method executes, only Razor Page filters enable access to HttpContext and the page. ミドルウェアは HttpContext にアクセスできますが、"ページ コンテキスト" にはアクセスできません。Middleware has access to the HttpContext, but not to the "page context". フィルターには、HttpContext へのアクセスを提供する派生型のパラメーター FilterContext があります。Filters have a FilterContext derived parameter, which provides access to HttpContext. ページ フィルターの例を次に示します。応答にヘッダーが追加するフィルター属性を実装する。これは、コンストラクターやミドルウェアでは実行できません。Here's a sample for a page filter: Implement a filter attribute that adds a header to the response, something that can't be done with constructors or middleware. ページとそのモデルのインスタンスへのアクセスを含む、ページ コンテキストへのアクセスは、フィルター、ハンドラー、Razor ページの本文を実行するときにのみ利用できます。Access to the page context, which includes access to the instances of the page and it's model, are only available when executing filters, handlers, or the body of a Razor Page.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

Razor ページ フィルターには、グローバルまたはページ レベルで適用できる次のメソッドがあります。Razor Page filters provide the following methods, which can be applied globally or at the page level:

  • 同期メソッド:Synchronous methods:

    • OnPageHandlerSelected:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に呼び出されます。OnPageHandlerSelected : Called after a handler method has been selected, but before model binding occurs.
    • OnPageHandlerExecuting:ハンドラー メソッドが実行される前の、モデル バインドが完了した後に呼び出されます。OnPageHandlerExecuting : Called before the handler method executes, after model binding is complete.
    • OnPageHandlerExecuted:ハンドラー メソッドが実行された後の、アクションの結果の前に呼び出されます。OnPageHandlerExecuted : Called after the handler method executes, before the action result.
  • 非同期メソッド:Asynchronous methods:

    • OnPageHandlerSelectionAsync:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に非同期で呼び出されます。OnPageHandlerSelectionAsync : Called asynchronously after the handler method has been selected, but before model binding occurs.
    • OnPageHandlerExecutionAsync:ハンドラー メソッドが呼び出される前の、モデル バインドの完了後に非同期で呼び出されます。OnPageHandlerExecutionAsync : Called asynchronously before the handler method is invoked, after model binding is complete.

フィルター インターフェイスの同期と非同期バージョンの 両方ではなくいずれか を実装します。Implement either the synchronous or the async version of a filter interface, not both. フレームワークは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。The framework checks first to see if the filter implements the async interface, and if so, it calls that. していない場合は、同期インターフェイスのメソッドを呼び出します。If not, it calls the synchronous interface's method(s). 両方のインターフェイスを実装した場合、非同期メソッドのみが呼び出されます。If both interfaces are implemented, only the async methods are called. ページのオーバーライドでもこの規則は同じです。オーバーライドの同期バージョンまたは非同期バージョンを実装でき、両方はできません。The same rule applies to overrides in pages, implement the synchronous or the async version of the override, not both.

Razor ページにフィルターをグローバルに実装するImplement Razor Page filters globally

IAsyncPageFilter は、次のコードによって実装されます。The following code implements IAsyncPageFilter:

public class SampleAsyncPageFilter : IAsyncPageFilter
{
    private readonly IConfiguration _config;

    public SampleAsyncPageFilter(IConfiguration config)
    {
        _config = config;
    }

    public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
    {
        var key = _config["UserAgentID"];
        context.HttpContext.Request.Headers.TryGetValue("user-agent",
                                                        out StringValues value);
        ProcessUserAgent.Write(context.ActionDescriptor.DisplayName,
                               "SampleAsyncPageFilter.OnPageHandlerSelectionAsync",
                               value, key.ToString());

        return Task.CompletedTask;
    }

    public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context,
                                                  PageHandlerExecutionDelegate next)
    {
        // Do post work.
        await next.Invoke();
    }
}

上記のコードでは、ProcessUserAgent.Write が、ユーザー エージェント文字列を使用して機能するユーザー指定のコードです。In the preceding code, ProcessUserAgent.Write is user supplied code that works with the user agent string.

次のコードは、Startup クラスで SampleAsyncPageFilter を有効にします。The following code enables the SampleAsyncPageFilter in the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.Filters.Add(new SampleAsyncPageFilter(Configuration));
        });
}

次のコードでは、AddFolderApplicationModelConvention を呼び出して、 /Movies 内のページにのみ SampleAsyncPageFilter を適用しています。The following code calls AddFolderApplicationModelConvention to apply the SampleAsyncPageFilter to only pages in /Movies:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options =>
    {
        options.Conventions.AddFolderApplicationModelConvention(
            "/Movies",
            model => model.Filters.Add(new SampleAsyncPageFilter(Configuration)));
    });
}

次のコードは、同期 IPageFilter を実装します。The following code implements the synchronous IPageFilter:

public class SamplePageFilter : IPageFilter
{
    private readonly IConfiguration _config;

    public SamplePageFilter(IConfiguration config)
    {
        _config = config;
    }

    public void OnPageHandlerSelected(PageHandlerSelectedContext context)
    {
        var key = _config["UserAgentID"];
        context.HttpContext.Request.Headers.TryGetValue("user-agent", out StringValues value);
        ProcessUserAgent.Write(context.ActionDescriptor.DisplayName,
                               "SamplePageFilter.OnPageHandlerSelected",
                               value, key.ToString());
    }

    public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
    {
        Debug.WriteLine("Global sync OnPageHandlerExecuting called.");
    }

    public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
    {
        Debug.WriteLine("Global sync OnPageHandlerExecuted called.");
    }
}

次のコードは、SamplePageFilter を有効にします。The following code enables the SamplePageFilter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.Filters.Add(new SamplePageFilter(Configuration));
        });
}

フィルター メソッドをオーバーライドして Razor ページにフィルターを実装するImplement Razor Page filters by overriding filter methods

次のコードでは、非同期 Razor ページ フィルターをオーバーライドしています。The following code overrides the asynchronous Razor Page filters:

public class IndexModel : PageModel
{
    private readonly IConfiguration _config;

    public IndexModel(IConfiguration config)
    {
        _config = config;
    }

    public override Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
    {
        Debug.WriteLine("/IndexModel OnPageHandlerSelectionAsync");
        return Task.CompletedTask;
    }

    public async override Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, 
                                                           PageHandlerExecutionDelegate next)
    {
        var key = _config["UserAgentID"];
        context.HttpContext.Request.Headers.TryGetValue("user-agent", out StringValues value);
        ProcessUserAgent.Write(context.ActionDescriptor.DisplayName,
                               "/IndexModel-OnPageHandlerExecutionAsync",
                                value, key.ToString());

        await next.Invoke();
    }
}

フィルター属性を実装するImplement a filter attribute

組み込みの属性ベースのフィルターである OnResultExecutionAsync フィルターは、サブクラス化できます。The built-in attribute-based filter OnResultExecutionAsync filter can be subclassed. 次のフィルターは、応答にヘッダーを追加します。The following filter adds a header to the response:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

namespace PageFilter.Filters
{
    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 });
        }
    }
}

次のコードは、AddHeader 属性を追加します。The following code applies the AddHeader attribute:

using Microsoft.AspNetCore.Mvc.RazorPages;
using PageFilter.Filters;

namespace PageFilter.Movies
{
    [AddHeader("Author", "Rick")]
    public class TestModel : PageModel
    {
        public void OnGet()
        {

        }
    }
}

ブラウザー開発者ツールなどのツールを使用して、ヘッダーを調べます。Use a tool such as the browser developer tools to examine the headers. [応答ヘッダー] の下に author: Rick が表示されます。Under Response Headers, author: Rick is displayed.

順序をオーバーライドする手順については、「既定の順序のオーバーライド」を参照してください。See Overriding the default order for instructions on overriding the order.

フィルターからフィルター パイプラインをショート サーキットする手順については、「キャンセルとショート サーキット」を参照してください。See Cancellation and short circuiting for instructions to short-circuit the filter pipeline from a filter.

Authorize フィルター属性Authorize filter attribute

PageModelAuthorize 属性を適用できます。The Authorize attribute can be applied to a PageModel:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace PageFilter.Pages
{
    [Authorize]
    public class ModelWithAuthFilterModel : PageModel
    {
        public IActionResult OnGet() => Page();
    }
}

作成者: Rick AndersonBy Rick Anderson

Razor ページのフィルターである IPageFilter および IAsyncPageFilter を使用すると、Razor ページ ハンドラーの実行の前後に Razor Pages でコードを実行できます。Razor Page filters IPageFilter and IAsyncPageFilter allow Razor Pages to run code before and after a Razor Page handler is run. Razor ページ フィルターは、個々のページ ハンドラー メソッドに適用できないことを除き、ASP.NET Core MVC アクション フィルターと類似しています。Razor Page filters are similar to ASP.NET Core MVC action filters, except they can't be applied to individual page handler methods.

Razor ページ フィルターRazor Page filters:

  • モデルのバインドが行われる前の、ハンドラー メソッドが選択された後にコードを実行します。Run code after a handler method has been selected, but before model binding occurs.
  • モデルのバインドの完了後の、ハンドラー メソッドの実行前にコードを実行します。Run code before the handler method executes, after model binding is complete.
  • ハンドラー メソッドの実行後にコードを実行します。Run code after the handler method executes.
  • ページまたはグローバルに実装できます。Can be implemented on a page or globally.
  • 特定のページ ハンドラー メソッドには適用できません。Cannot be applied to specific page handler methods.

コードは、ページ コンストラクターまたはミドルウェアを使用してハンドラー メソッドの実行前に実行できますが、HttpContext にアクセスできるのは Razor ページ フィルターのみです。Code can be run before a handler method executes using the page constructor or middleware, but only Razor Page filters have access to HttpContext. フィルターには、HttpContext へのアクセスを提供する FilterContext 派生のパラメーターがあります。Filters have a FilterContext derived parameter, which provides access to HttpContext. たとえば、「フィルター属性を実装する」のサンプルでは、応答にヘッダーが追加されます。これは、コンストラクターやミドルウェアでは実行できません。For example, the Implement a filter attribute sample adds a header to the response, something that can't be done with constructors or middleware.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

Razor ページ フィルターには、グローバルまたはページ レベルで適用できる次のメソッドがあります。Razor Page filters provide the following methods, which can be applied globally or at the page level:

  • 同期メソッド:Synchronous methods:

    • OnPageHandlerSelected:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に呼び出されます。OnPageHandlerSelected : Called after a handler method has been selected, but before model binding occurs.
    • OnPageHandlerExecuting:ハンドラー メソッドが実行される前の、モデル バインドが完了した後に呼び出されます。OnPageHandlerExecuting : Called before the handler method executes, after model binding is complete.
    • OnPageHandlerExecuted:ハンドラー メソッドが実行された後の、アクションの結果の前に呼び出されます。OnPageHandlerExecuted : Called after the handler method executes, before the action result.
  • 非同期メソッド:Asynchronous methods:

    • OnPageHandlerSelectionAsync:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に非同期で呼び出されます。OnPageHandlerSelectionAsync : Called asynchronously after the handler method has been selected, but before model binding occurs.
    • OnPageHandlerExecutionAsync:ハンドラー メソッドが呼び出される前の、モデル バインドの完了後に非同期で呼び出されます。OnPageHandlerExecutionAsync : Called asynchronously before the handler method is invoked, after model binding is complete.

注意

フィルター インターフェイスの同期と非同期バージョンの両方ではなく、いずれか を実装します。Implement either the synchronous or the async version of a filter interface, not both. フレームワークは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。The framework checks first to see if the filter implements the async interface, and if so, it calls that. していない場合は、同期インターフェイスのメソッドを呼び出します。If not, it calls the synchronous interface's method(s). 両方のインターフェイスを実装した場合、非同期メソッドのみが呼び出されます。If both interfaces are implemented, only the async methods are called. ページのオーバーライドでもこの規則は同じです。オーバーライドの同期バージョンまたは非同期バージョンを実装でき、両方はできません。The same rule applies to overrides in pages, implement the synchronous or the async version of the override, not both.

Razor ページにフィルターをグローバルに実装するImplement Razor Page filters globally

IAsyncPageFilter は、次のコードによって実装されます。The following code implements IAsyncPageFilter:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace PageFilter.Filters
{
    public class SampleAsyncPageFilter : IAsyncPageFilter
    {
        private readonly ILogger _logger;

        public SampleAsyncPageFilter(ILogger logger)
        {
            _logger = logger;
        }

        public async Task OnPageHandlerSelectionAsync(
                                            PageHandlerSelectedContext context)
        {
            _logger.LogDebug("Global OnPageHandlerSelectionAsync called.");
            await Task.CompletedTask;
        }

        public async Task OnPageHandlerExecutionAsync(
                                            PageHandlerExecutingContext context,
                                            PageHandlerExecutionDelegate next)
        {
            _logger.LogDebug("Global OnPageHandlerExecutionAsync called.");
            await next.Invoke();
        }
    }
}

前述のコードでは、ILogger は不要です。In the preceding code, ILogger is not required. これは、アプリケーションのトレース情報を提供するためにサンプルで使用されています。It's used in the sample to provide trace information for the application.

次のコードは、Startup クラスで SampleAsyncPageFilter を有効にします。The following code enables the SampleAsyncPageFilter in the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new SampleAsyncPageFilter(_logger));
    });
}

次は、完全な Startup クラスのコードです。The following code shows the complete Startup class:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PageFilter.Filters;

namespace PageFilter
{
    public class Startup
    {
        ILogger _logger;
        public Startup(ILoggerFactory loggerFactory, IConfiguration configuration)
        {
            _logger = loggerFactory.CreateLogger<GlobalFiltersLogger>();
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(options =>
            {
                options.Filters.Add(new SampleAsyncPageFilter(_logger));
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc();
        }
    }
}

次のコードは、AddFolderApplicationModelConvention を呼び出し、 /subFolder のページにのみ SampleAsyncPageFilter を適用します。The following code calls AddFolderApplicationModelConvention to apply the SampleAsyncPageFilter to only pages in /subFolder:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
       .AddRazorPagesOptions(options =>
       {
           options.Conventions.AddFolderApplicationModelConvention(
               "/subFolder",
               model => model.Filters.Add(new SampleAsyncPageFilter(_logger)));
       });
}

次のコードは、同期 IPageFilter を実装します。The following code implements the synchronous IPageFilter:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace PageFilter.Filters
{
    public class SamplePageFilter : IPageFilter
    {
        private readonly ILogger _logger;

        public SamplePageFilter(ILogger logger)
        {
            _logger = logger;
        }

        public void OnPageHandlerSelected(PageHandlerSelectedContext context)
        {
            _logger.LogDebug("Global sync OnPageHandlerSelected called.");
        }

        public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
        {
            _logger.LogDebug("Global sync PageHandlerExecutingContext called.");
        }

        public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
        {
            _logger.LogDebug("Global sync OnPageHandlerExecuted called.");
        }
    }
}

次のコードは、SamplePageFilter を有効にします。The following code enables the SamplePageFilter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new SamplePageFilter(_logger));
    });
}

フィルター メソッドをオーバーライドして Razor ページにフィルターを実装するImplement Razor Page filters by overriding filter methods

次のコードでは、同期 Razor ページ フィルターをオーバーライドしています。The following code overrides the synchronous Razor Page filters:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace PageFilter.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger _logger;

        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }
        public string Message { get; set; }

        public void OnGet()
        {
            _logger.LogDebug("IndexModel/OnGet");
        }
        
        public override void OnPageHandlerSelected(
                                    PageHandlerSelectedContext context)
        {
            _logger.LogDebug("IndexModel/OnPageHandlerSelected");          
        }

        public override void OnPageHandlerExecuting(
                                    PageHandlerExecutingContext context)
        {
            Message = "Message set in handler executing";
            _logger.LogDebug("IndexModel/OnPageHandlerExecuting");
        }


        public override void OnPageHandlerExecuted(
                                    PageHandlerExecutedContext context)
        {
            _logger.LogDebug("IndexModel/OnPageHandlerExecuted");
        }
    }
}

フィルター属性を実装するImplement a filter attribute

組み込みの属性ベースのフィルターである OnResultExecutionAsync フィルターはサブクラス化することができます。The built-in attribute-based filter OnResultExecutionAsync filter can be subclassed. 次のフィルターは、応答にヘッダーを追加します。The following filter adds a header to the response:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

namespace PageFilter.Filters
{
    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 });
        }
    }
}

次のコードは、AddHeader 属性を追加します。The following code applies the AddHeader attribute:

[AddHeader("Author", "Rick")]
public class ContactModel : PageModel
{
    private readonly ILogger _logger;

    public ContactModel(ILogger<ContactModel> logger)
    {
        _logger = logger;
    }
    public string Message { get; set; }

    public async Task OnGetAsync()
    {
        Message = "Your contact page.";
        _logger.LogDebug("Contact/OnGet");
        await Task.CompletedTask;
    }
}

順序をオーバーライドする手順については、「既定の順序のオーバーライド」を参照してください。See Overriding the default order for instructions on overriding the order.

フィルターからフィルター パイプラインをショート サーキットする手順については、「キャンセルとショート サーキット」を参照してください。See Cancellation and short circuiting for instructions to short-circuit the filter pipeline from a filter.

Authorize フィルター属性Authorize filter attribute

PageModelAuthorize 属性を適用できます。The Authorize attribute can be applied to a PageModel:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace PageFilter.Pages
{
    [Authorize]
    public class ModelWithAuthFilterModel : PageModel
    {
        public IActionResult OnGet() => Page();
    }
}