ASP.NET Core の Razor Pages のフィルター メソッド
作成者: Rick Anderson
Razor ページ フィルター IPageFilter と IAsyncPageFilter を使うと、Razor Pages で Razor ページ ハンドラーの実行前と実行後にコードを実行できるようになります。 Razor ページ フィルターは、個々のページ ハンドラー メソッドに適用できないことを除き、ASP.NET Core MVC アクション フィルターと類似しています。
Razor ページ フィルター
- モデルのバインドが行われる前の、ハンドラー メソッドが選択された後にコードを実行します。
- モデルのバインドの完了後の、ハンドラー メソッドの実行前にコードを実行します。
- ハンドラー メソッドの実行後にコードを実行します。
- ページまたはグローバルに実装できます。
- 特定のページ ハンドラー メソッドには適用できません。
- コンストラクターの依存関係は、依存関係の挿入 (DI) によって入力されるようにできます。 詳細については、「ServiceFilterAttribute」と「TypeFilterAttribute」を参照してください。
ページ コンストラクターとミドルウェアにより、ハンドラー メソッドが実行される前にカスタム コードの実行が可能になりますが、HttpContext とページへのアクセスを可能にするのは Razor ページ フィルターのみです。 ミドルウェアは HttpContext
にアクセスできますが、"ページ コンテキスト" にはアクセスできません。 フィルターには、HttpContext
へのアクセスを提供する派生型のパラメーター FilterContext があります。 ページ フィルターの例を次に示します。応答にヘッダーが追加するフィルター属性を実装する。これは、コンストラクターやミドルウェアでは実行できません。 ページとそのモデルのインスタンスへのアクセスを含む、ページ コンテキストへのアクセスは、フィルター、ハンドラー、Razor ページの本文を実行するときにのみ利用できます。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
Razor ページ フィルターには、グローバルまたはページ レベルで適用できる次のメソッドがあります。
同期メソッド:
- OnPageHandlerSelected:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に呼び出されます。
- OnPageHandlerExecuting:ハンドラー メソッドが実行される前の、モデル バインドが完了した後に呼び出されます。
- OnPageHandlerExecuted:ハンドラー メソッドが実行された後の、アクションの結果の前に呼び出されます。
非同期メソッド:
- OnPageHandlerSelectionAsync:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に非同期で呼び出されます。
- OnPageHandlerExecutionAsync:ハンドラー メソッドが呼び出される前の、モデル バインドの完了後に非同期で呼び出されます。
フィルター インターフェイスの同期と非同期バージョンの両方ではなく、いずれかを実装します。 フレームワークは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。 していない場合は、同期インターフェイスのメソッドを呼び出します。 両方のインターフェイスを実装した場合、非同期メソッドのみが呼び出されます。 ページのオーバーライドでもこの規則は同じです。オーバーライドの同期バージョンまたは非同期バージョンを実装でき、両方はできません。
Razor ページにフィルターをグローバルに実装する
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
が、ユーザー エージェント文字列を使用して機能するユーザー指定のコードです。
次のコードは、Startup
クラスで SampleAsyncPageFilter
を有効にします。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.Filters.Add(new SampleAsyncPageFilter(Configuration));
});
}
次のコードでは、AddFolderApplicationModelConvention を呼び出して、 /Movies 内のページにのみ SampleAsyncPageFilter
を適用しています。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AddFolderApplicationModelConvention(
"/Movies",
model => model.Filters.Add(new SampleAsyncPageFilter(Configuration)));
});
}
次のコードは、同期 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
を有効にします。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages()
.AddMvcOptions(options =>
{
options.Filters.Add(new SamplePageFilter(Configuration));
});
}
フィルター メソッドをオーバーライドして Razor ページにフィルターを実装する
次のコードでは、非同期 Razor ページ フィルターをオーバーライドしています。
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();
}
}
フィルター属性を実装する
組み込みの属性ベースのフィルターである OnResultExecutionAsync フィルターは、サブクラス化できます。 次のフィルターは、応答にヘッダーを追加します。
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
属性を追加します。
using Microsoft.AspNetCore.Mvc.RazorPages;
using PageFilter.Filters;
namespace PageFilter.Movies
{
[AddHeader("Author", "Rick")]
public class TestModel : PageModel
{
public void OnGet()
{
}
}
}
ブラウザー開発者ツールなどのツールを使用して、ヘッダーを調べます。 [応答ヘッダー] の下に author: Rick
が表示されます。
順序をオーバーライドする手順については、「既定の順序のオーバーライド」を参照してください。
フィルターからフィルター パイプラインをショート サーキットする手順については、「キャンセルとショート サーキット」を参照してください。
Authorize フィルター属性
PageModel
に Authorize 属性を適用できます。
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 Anderson
Razor ページ フィルター IPageFilter と IAsyncPageFilter を使うと、Razor Pages で Razor ページ ハンドラーの実行前と実行後にコードを実行できるようになります。 Razor ページ フィルターは、個々のページ ハンドラー メソッドに適用できないことを除き、ASP.NET Core MVC アクション フィルターと類似しています。
Razor ページ フィルター
- モデルのバインドが行われる前の、ハンドラー メソッドが選択された後にコードを実行します。
- モデルのバインドの完了後の、ハンドラー メソッドの実行前にコードを実行します。
- ハンドラー メソッドの実行後にコードを実行します。
- ページまたはグローバルに実装できます。
- 特定のページ ハンドラー メソッドには適用できません。
コードは、ページ コンストラクターまたはミドルウェアを使用してハンドラー メソッドの実行前に実行できますが、HttpContext にアクセスできるのは Razor ページ フィルターのみです。 フィルターには、HttpContext
へのアクセスを提供する派生型のパラメーター FilterContext があります。 たとえば、「フィルター属性を実装する」のサンプルでは、応答にヘッダーが追加されます。これは、コンストラクターやミドルウェアでは実行できません。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
Razor ページ フィルターには、グローバルまたはページ レベルで適用できる次のメソッドがあります。
同期メソッド:
- OnPageHandlerSelected:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に呼び出されます。
- OnPageHandlerExecuting:ハンドラー メソッドが実行される前の、モデル バインドが完了した後に呼び出されます。
- OnPageHandlerExecuted:ハンドラー メソッドが実行された後の、アクションの結果の前に呼び出されます。
非同期メソッド:
- OnPageHandlerSelectionAsync:ハンドラー メソッドが選択された後の、モデル バインドが行われる前に非同期で呼び出されます。
- OnPageHandlerExecutionAsync:ハンドラー メソッドが呼び出される前の、モデル バインドの完了後に非同期で呼び出されます。
Note
フィルター インターフェイスの同期と非同期バージョンの両方ではなく、いずれかを実装します。 フレームワークは、最初にフィルターが非同期インターフェイスを実装しているかどうかをチェックして、している場合はそれを呼び出します。 していない場合は、同期インターフェイスのメソッドを呼び出します。 両方のインターフェイスを実装した場合、非同期メソッドのみが呼び出されます。 ページのオーバーライドでもこの規則は同じです。オーバーライドの同期バージョンまたは非同期バージョンを実装でき、両方はできません。
Razor ページにフィルターをグローバルに実装する
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 は不要です。 これは、アプリケーションのトレース情報を提供するためにサンプルで使用されています。
次のコードは、Startup
クラスで SampleAsyncPageFilter
を有効にします。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new SampleAsyncPageFilter(_logger));
});
}
次は、完全な Startup
クラスのコードです。
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
を適用します。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AddFolderApplicationModelConvention(
"/subFolder",
model => model.Filters.Add(new SampleAsyncPageFilter(_logger)));
});
}
次のコードは、同期 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
を有効にします。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new SamplePageFilter(_logger));
});
}
フィルター メソッドをオーバーライドして Razor ページにフィルターを実装する
次のコードでは、同期 Razor ページ フィルターをオーバーライドしています。
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");
}
}
}
フィルター属性を実装する
組み込みの属性ベースのフィルターである OnResultExecutionAsync フィルターは、サブクラス化できます。 次のフィルターは、応答にヘッダーを追加します。
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
属性を追加します。
[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;
}
}
順序をオーバーライドする手順については、「既定の順序のオーバーライド」を参照してください。
フィルターからフィルター パイプラインをショート サーキットする手順については、「キャンセルとショート サーキット」を参照してください。
Authorize フィルター属性
PageModel
に Authorize 属性を適用できます。
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();
}
}
ASP.NET Core