ASP.NET Core 中页面的筛选方法Razor

作者:Rick Anderson

Razor 页面筛选器 IPageFilter 允许 IAsyncPageFilterRazor 页面在 Razor 页面处理程序运行之前和之后运行代码。 Razor 页面筛选器类似于 ASP.NET 核心 MVC作筛选器,但不能应用于单个页面处理程序方法。

Razor 页面筛选器:

  • 在选择处理程序方法之后运行代码,但在模型绑定发生之前。
  • 在完成模型绑定后,在处理程序方法执行之前运行代码。
  • 在处理程序方法执行后运行代码。
  • 可以在页面上或全局实现。
  • 不能应用于特定的页面处理程序方法。
  • 可以具有 由依赖关系注入 (DI)填充的构造函数依赖项。 有关详细信息,请参阅 ServiceFilterAttributeTypeFilterAttribute

虽然页面构造函数和中间件能够在执行处理方法之前启用自定义代码执行,但只有通过 Razor 页面筛选器才能访问 HttpContext 页面和整个页面。 中间件可以访问 HttpContext,但无法访问“页面上下文”。 筛选器具有 FilterContext 派生参数,该参数提供对 HttpContext 的访问。 下面是页面筛选器的示例: 实现一个筛选器属性 ,该属性将标头添加到响应,这是无法使用构造函数或中间件完成的。 仅当执行筛选器、处理程序或页面正文 Razor 时,才能访问页面上下文(包括对页面实例及其模型的访问)。

查看或下载示例代码如何下载

Razor 页面筛选器提供以下方法,这些方法可全局应用或在页面级别应用:

实现筛选器接口时,只需选择同步或异步版本中的一个,而不需要同时实现两个版本。 框架首先检查筛选器是否实现异步接口,如果是,则调用该接口。 如果没有,它将调用同步接口的方法。 如果实现这两个接口,则只调用异步方法。 相同的规则适用于页面中的替代,实现替代的同步版本或异步版本,而不是同时实现。

全局实现 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 用户提供的代码适用于用户代理字符串。

以下代码将在 SampleAsyncPageFilter 类中启用 Startup

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 页面筛选。

以下代码替代异步 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

有关替代顺序的说明,请参阅 重写默认顺序

有关取消和中断筛选器管道的说明,请参阅取消和中断

授权筛选器属性

授权属性可以应用于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 页面筛选器 IPageFilter 允许 IAsyncPageFilterRazor 页面在 Razor 页面处理程序运行之前和之后运行代码。 Razor 页面筛选器类似于 ASP.NET 核心 MVC作筛选器,但不能应用于单个页面处理程序方法。

Razor 页面筛选器:

  • 在选择处理程序方法之后运行代码,但在模型绑定发生之前。
  • 在完成模型绑定后,在处理程序方法执行之前运行代码。
  • 在处理程序方法执行后运行代码。
  • 可以在页面上或全局实现。
  • 不能应用于特定的页面处理程序方法。

代码可以在处理程序方法使用页面构造函数或中间件执行之前运行,但只有 Razor 页面筛选器有权访问 HttpContext。 筛选器具有 FilterContext 派生参数,该参数提供对 HttpContext 的访问。 例如, 实现筛选器属性 示例将标头添加到响应,这是无法通过构造函数或中间件完成的。

查看或下载示例代码如何下载

Razor 页面筛选器提供以下方法,这些方法可全局应用或在页面级别应用:

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 不需要。 该示例用于为应用程序提供跟踪信息。

以下代码将在 SampleAsyncPageFilter 类中启用 Startup

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 页面筛选。

以下代码替代同步 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;
    }
}

有关替代顺序的说明,请参阅 重写默认顺序

有关取消和中断筛选器管道的说明,请参阅取消和中断

授权筛选器属性

授权属性可以应用于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();
    }
}