ASP.NET Core 中 Razor Pages 的篩選方法

作者:Rick Anderson

Razor Page 篩選 IPageFilterIAsyncPageFilter 允許 Razor Pages 在執行 Razor Page 處理常式之前和之後執行程式碼。 Razor Page 篩選與 ASP.NET Core MVC 動作篩選類似,但無法套用至個別頁面處理常式方法。

Razor Page 篩選:

  • 在選取處理常式方法之後,但在進行模型繫結之前執行程式碼。
  • 在執行處理常式方法之前,並在完成模型繫結之後執行程式碼。
  • 在執行處理常式方法之後執行程式碼。
  • 可以在某個頁面或全域實作。
  • 無法套用至特定頁面處理常式方法。
  • 可以透過相依性插入 (DI) 來填入建構函式相依性。 如需詳細資訊,請參閱 ServiceFilterAttributeTypeFilterAttribute

雖然頁面建構函式和中介軟體可在處理常式方法執行之前啟用執行自訂程式碼,但只有 Razor Page 篩選可以啟用對 HttpContext 和頁面的存取。 中介軟體可以存取 HttpContext,但無法存取「頁面內容」。 篩選具有 FilterContext 衍生參數,可提供對 HttpContext 的存取權。 以下是頁面篩選範例:實作篩選屬性,可將標頭新增至回應,而這無法使用建構函式或中介軟體所完成。 只有在執行篩選、處理常式或 Razor 頁面主體時,才能使用頁面內容的存取權 (包括頁面執行個體和其模型的存取權)。

檢視或下載範例程式碼 \(英文\) (如何下載)

Razor Page 篩選提供下列方法,以在全域或頁面層級套用:

請實作同步非同步版本的篩選條件介面,而不要同時實作這兩者。 架構會先檢查以查看篩選條件是否實作非同步介面,如果是的話,便呼叫該介面。 如果沒有,它會呼叫同步介面的方法。 如果實作這兩個介面,則只會呼叫非同步方法。 相同的規則會套用至頁面中的覆寫,實作覆寫的同步或非同步版本,但不能同時實作。

全域實作 Razor Page 篩選

下列程式碼會實作 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,只將 SampleAsyncPageFilter 套用至 /Movies 中的頁面:

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 Page 篩選

下列程式碼會覆寫非同步 Razor Page 篩選:

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

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 Page 篩選 IPageFilterIAsyncPageFilter 允許 Razor Pages 在執行 Razor Page 處理常式之前和之後執行程式碼。 Razor Page 篩選與 ASP.NET Core MVC 動作篩選類似,但無法套用至個別頁面處理常式方法。

Razor Page 篩選:

  • 在選取處理常式方法之後,但在進行模型繫結之前執行程式碼。
  • 在執行處理常式方法之前,並在完成模型繫結之後執行程式碼。
  • 在執行處理常式方法之後執行程式碼。
  • 可以在某個頁面或全域實作。
  • 無法套用至特定頁面處理常式方法。

使用頁面建構函式或中介軟體來執行處理常式方法之前,可以執行程式碼,但只有 Razor 頁面篩選才能存取 HttpContext。 篩選具有 FilterContext 衍生參數,可提供對 HttpContext 的存取權。 例如,實作篩選條件屬性範例會將標頭新增至回應,這是無法使用建構函式或中介軟體完成的作業。

檢視或下載範例程式碼 \(英文\) (如何下載)

Razor Page 篩選提供下列方法,以在全域或頁面層級套用:

注意

請實作同步非同步版本的篩選條件介面,但不要兩者同時實作。 架構會先檢查以查看篩選條件是否實作非同步介面,如果是的話,便呼叫該介面。 如果沒有,它會呼叫同步介面的方法。 如果實作這兩個介面,則只會呼叫非同步方法。 相同的規則會套用至頁面中的覆寫,實作覆寫的同步或非同步版本,但不能同時實作。

全域實作 Razor Page 篩選

下列程式碼會實作 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,只將 SampleAsyncPageFilter 套用至 /subFolder 中的頁面:

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 Page 篩選

下列程式碼會覆寫同步 Razor Page 篩選:

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

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