https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2

通过使用 ASP.NET Core 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码。

内置筛选器处理任务,例如:

  • 授权(防止用户访问未获授权的资源)。
  • 响应缓存(对请求管道进行短路出路,以便返回缓存的响应)。

例如,错误处理异常筛选器可以合并错误处理。

本文档适用于 Razor Pages、API 控制器和具有视图的控制器。

如何下载)。

筛选器的工作原理

筛选器管道在 ASP.NET Core 选择了要执行的操作之后运行。

ASP.NET Core 中的筛选器

筛选器类型

每种筛选器类型都在筛选器管道中的不同阶段执行:

  • 如果请求未获授权,授权筛选器可以让管道短路。

  • 资源筛选器:

    • 授权后运行。
    • 例如,OnResourceExecuting 可以在模型绑定之前运行代码。
    • OnResourceExecuted 可以在管道的其余阶段完成之后运行代码。
  • 不可在 Razor Pages 中使用操作筛选器。

  • 异常筛选器用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。

  • 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。

下图展示了筛选器类型在筛选器管道中的交互方式。

ASP.NET Core 中的筛选器

实现

通过不同的接口定义,筛选器同时支持同步和异步实现。

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

每个 On-Stage-ExecutionAsync 方法采用执行筛选器的管道阶段的 FilterType-ExecutionDelegate

多个筛选器阶段

ActionFilterAttribute 类实现 IActionFilterIResultFilter 及其异步等效接口。

ActionFilterAttribute),将为每种筛选器类型仅重写同步方法或仅重写异步方法。

内置筛选器属性

例如,以下结果筛选器会向响应添加标头:

复制
 
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.");
    }

多种筛选器接口具有相应属性,这些属性可用作自定义实现的基类。

筛选器属性:

筛选器作用域和执行顺序

可以将筛选器添加到管道中的三个作用域之一:

  • 在操作上使用属性。
  • 在控制器上使用属性。
  • 所有控制器和操作的全局筛选器,如下面的代码所示:
复制
 
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 集合全局添加三个筛选器。

默认执行顺序

类筛选器涵盖方法筛选器。

筛选器序列:

  • 全局筛选器的 before 代码。
    • 控制器筛选器的 before 代码。
      • 操作方法筛选器的 before 代码。
      • 操作方法筛选器的 after 代码。
    • 控制器筛选器的 after 代码。
  • 全局筛选器的 after 代码。

下面的示例阐释了为同步操作筛选器调用筛选器方法的顺序。

TABLE 2
序列 筛选器作用域 筛选器方法
1 Global OnActionExecuting
2 控制器 OnActionExecuting
3 方法 OnActionExecuting
4 方法 OnActionExecuted
5 控制器 OnActionExecuted
6 Global OnActionExecuted

此序列显示:

  • 方法筛选器已嵌套在控制器筛选器中。
  • 控制器筛选器已嵌套在全局筛选器中。

控制器和 Razor 页面级筛选器

这些方法:

  • 覆盖为给定操作运行的筛选器。
  • OnActionExecuting 在所有操作筛选器之前调用。
  • OnActionExecuted 在所有操作筛选器之后调用。
  • next 之后的筛选器中的代码在操作方法之后运行。

例如,在下载示例中,启动时全局应用 MySampleActionFilter

TestController

  • 将 SampleActionFilterAttribute ([SampleActionFilter]) 应用于 FilterTest2 操作。
  • 重写 OnActionExecuting 和 OnActionExecuted
复制
 
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 页面筛选器。

重写默认顺序

具有较低的 Order 值的筛选器:

  • 在具有较高的 Order 值的筛选器之前运行 before 代码。
  • 在具有较高的 Order 值的筛选器之后运行 after 代码。

可以使用构造函数参数设置 Order 属性:

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

如果控制器和全局筛选器的 Order 属性分别设置为 1 和 2,则会反转执行顺序。

TABLE 3
序列 筛选器作用域 Order 属性 筛选器方法
1 方法 0 OnActionExecuting
2 控制器 1 OnActionExecuting
3 Global 2 OnActionExecuting
4 Global 2 OnActionExecuted
5 控制器 1 OnActionExecuted
6 方法 0 OnActionExecuted

对于内置筛选器,作用域会确定顺序,除非将 Order 设为非零值。

取消和设置短路

例如,以下资源筛选器将阻止执行管道的其余阶段:

复制
 
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)
    {
    }
}

ShortCircuitingResourceFilter

  • 先运行,因为它是资源筛选器且 AddHeader 是操作筛选器。
  • 对管道的其余部分进行短路处理。

先运行 ShortCircuitingResourceFilter(考虑到它的筛选器类型),或显式使用 Order 属性。

复制
 
[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 提供的构造函数依赖项:

可以将前面的筛选器应用于控制器或操作方法:

添加到筛选器的日志记录:

  • 应重点关注业务域问题或特定于筛选器的行为。
  • 内置筛选器记录操作和框架事件。

ServiceFilterAttribute

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.IsReusable 设置:

  • ASP.NET Core 运行时不保证:

    • 将创建筛选器的单一实例。
    • 稍后不会从 DI 容器重新请求筛选器。
  • 不应与依赖于生命周期不同于单一实例的服务的筛选器一起使用。

CreateInstance 从 DI 中加载指定的类型。

TypeFilterAttribute

Microsoft.Extensions.DependencyInjection.ObjectFactory 对类型进行实例化。

因为不会直接从 DI 容器解析 TypeFilterAttribute 类型:

  • 它们具备由 DI 容器实现的依赖项。
  • TypeFilterAttribute 可以选择为类型接受构造函数参数。

TypeFilterAttribute.IsReusable 设置:

  • ASP.NET Core 运行时不保证将创建筛选器的单一实例。

  • 不应与依赖于生命周期不同于单一实例的服务的筛选器一起使用。

下面的示例演示如何使用 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)
    { }
}

授权筛选器

授权筛选器:

  • 是筛选器管道中运行的第一个筛选器。
  • 控制对操作方法的访问。
  • 具有在它之前的执行的方法,但没有之后执行的方法。

内置授权筛选器:

  • 调用授权系统。
  • 不授权请求。

不会在授权筛选器中引发异常:

  • 不会处理异常。
  • 异常筛选器不会处理异常。

在授权筛选器出现异常时请小心应对。

授权。

资源筛选器

资源筛选器:

  • IAsyncResourceFilter 接口。
  • 执行会覆盖筛选器管道的绝大部分。
  • 授权筛选器在资源筛选器之前运行。

例如,如果缓存命中,则缓存筛选器可以绕开管道的其余阶段。

资源筛选器示例:

  • 短路资源筛选器。

  • DisableFormValueModelBindingAttribute:

    • 可以防止模型绑定访问表单数据。
    • 用于上传大型文件,以防止表单数据被读入内存。

操作筛选器

 重要

Razor 页面的筛选方法。

操作筛选器:

  • 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 提供 Controller 和 Result 以及以下属性:

  • Canceled - 如果操作执行已被另一个筛选器设置短路,则为 true。

  • 将此属性设置为 null:

    • 有效地处理异常。
    • 执行 Result,从操作方法中将它返回。

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:

    • 有效地处理异常。
    • 执行 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);
    }
}

异常筛选器

异常筛选器:

  • IAsyncExceptionFilter。
  • 可用于实现常见的错误处理策略。

下面的异常筛选器示例使用自定义错误视图,显示在开发应用时发生的异常的相关详细信息:

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

异常筛选器:

  • 没有之前和之后的事件。
  • OnExceptionAsync。
  • 模型绑定、操作筛选器或操作方法中发生的未经处理的异常。
  • 请不要捕获资源筛选器、结果筛选器或 MVC 结果执行中发生的异常。

只有操作筛选器才能执行该转变。

异常筛选器:

  • 非常适合捕获发生在操作中的异常。
  • 并不像错误处理中间件那么灵活。

API 终结点可能返回 JSON 形式的错误信息,而基于视图的操作可能返回 HTML 形式的错误页。

结果筛选器

结果筛选器:

  • 实现接口:
    • IAsyncResultFilter
    • IAsyncAlwaysRunResultFilter
  • 它们的执行围绕着操作结果的执行。

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

操作结果。

不会在以下情况下执行结果筛选器:

  • 授权筛选器或资源筛选器使管道短路。
  • 异常筛选器通过生成操作结果来处理异常。

如果在 IResultFilter.OnResultExecuting 中引发异常,则会导致:

  • 阻止操作结果和后续筛选器的执行。
  • 结果被视为失败而不是成功。

如果响应已发送到客户端,则无法再更改。

如果操作结果执行已被另一个筛选器设置短路,则 ResultExecutedContext.Canceled 设置为 true

如果在操作结果引发异常时标头已刷新到客户端,则没有任何可靠的机制可用于发送失败代码。

ResultExecutingContext.Cancel 设置为 true,并且不调用 ResultExecutionDelegate

复制
 
public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

AddHeaderAttribute 类是一种结果筛选器属性。

IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter

这包括由以下对象生成的操作结果:

  • 设置短路的授权筛选器和资源筛选器。
  • 异常筛选器。

ObjectResult):

复制
 
public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == 415)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = 422,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactory

这提供了一种很灵活的设计,因为无需在应用启动时显式设置精确的筛选器管道。

可以使用自定义属性实现来实现 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 开发人员工具显示示例代码添加的以下响应标头:

  • author: Joe Smith
  • globaladdheader: Result filter added to MvcOptions.Filters
  • internal: My header

前面的代码创建 internal: My header 响应标头。

在属性上实现 IFilterFactory

实现 IFilterFactory 的筛选器可用于以下筛选器:

  • 不需要传递参数。
  • 具备需要由 DI 填充的构造函数依赖项。

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] 的三种方法:

复制
 
[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 上下文和构造。

下面的示例使用本地化中间件为请求建立当前区域性:

复制
 
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}");
}

中间件筛选器与资源筛选器在筛选器管道的相同阶段运行,即,在模型绑定之前以及管道的其余阶段之后。

后续操作

  • Razor Pages 的筛选器方法。
  • 下载、测试并修改 GitHub 示例。

相关文章: