ASP.NET Core 中的筛选器Filters in ASP.NET Core

作者:Kirk LarkinRick AndersonTom DykstraSteve SmithBy Kirk Larkin, Rick Anderson, Tom Dykstra, and Steve Smith

通过使用 ASP.NET Core 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码。 Filters in ASP.NET Core allow code to be run before or after specific stages in the request processing pipeline.

内置筛选器处理任务,例如:Built-in filters handle tasks such as:

  • 授权(防止用户访问未获授权的资源)。Authorization (preventing access to resources a user isn't authorized for).
  • 响应缓存(对请求管道进行短路出路,以便返回缓存的响应)。Response caching (short-circuiting the request pipeline to return a cached response).

可以创建自定义筛选器,用于处理横切关注点。Custom filters can be created to handle cross-cutting concerns. 横切关注点的示例包括错误处理、缓存、配置、授权和日志记录。Examples of cross-cutting concerns include error handling, caching, configuration, authorization, and logging. 筛选器可以避免复制代码。Filters avoid duplicating code. 例如,错误处理异常筛选器可以合并错误处理。For example, an error handling exception filter could consolidate error handling.

本文档适用于 Razor Pages、API 控制器和具有视图的控制器。This document applies to Razor Pages, API controllers, and controllers with views.

查看或下载示例如何下载)。View or download sample (how to download).

筛选器的工作原理How filters work

筛选器在 ASP.NET Core 操作调用管道 (有时称为筛选器管道 )内运行。Filters run within the ASP.NET Core action invocation pipeline, sometimes referred to as the filter pipeline. 筛选器管道在 ASP.NET Core 选择了要执行的操作之后运行。The filter pipeline runs after ASP.NET Core selects the action to execute.

请求通过其他中间件、路由中间件、操作选择和 ASP.NET Core 操作调用管道进行处理。

筛选器类型Filter types

每种筛选器类型都在筛选器管道中的不同阶段执行:Each filter type is executed at a different stage in the filter pipeline:

  • 授权筛选器最先运行,用于确定是否已针对请求为用户授权。Authorization filters run first and are used to determine whether the user is authorized for the request. 如果请求未获授权,授权筛选器可以让管道短路。Authorization filters short-circuit the pipeline if the request is unauthorized.

  • 资源筛选器Resource filters:

    • 授权后运行。Run after authorization.
    • OnResourceExecuting 可以在筛选器管道的其余阶段之前运行代码。OnResourceExecuting can run code before the rest of the filter pipeline. 例如,OnResourceExecuting 可以在模型绑定之前运行代码。For example, OnResourceExecuting can run code before model binding.
    • OnResourceExecuted 可以在管道的其余阶段完成之后运行代码。OnResourceExecuted can run code after the rest of the pipeline has completed.
  • 操作筛选器可以在调用单个操作方法之前和之后立即运行代码。Action filters can run code immediately before and after an individual action method is called. 它们可用于处理传入某个操作的参数以及从该操作返回的结果。They can be used to manipulate the arguments passed into an action and the result returned from the action. 不可在 Razor Pages 中使用操作筛选器 。Action filters are not supported in Razor Pages.

  • 异常筛选器用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。Exception filters are used to apply global policies to unhandled exceptions that occur before anything has been written to the response body.

  • 结果筛选器可以在执行单个操作结果之前和之后立即运行代码。Result filters can run code immediately before and after the execution of individual action results. 仅当操作方法成功执行时,它们才会运行。They run only when the action method has executed successfully. 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。They are useful for logic that must surround view or formatter execution.

下图展示了筛选器类型在筛选器管道中的交互方式。The following diagram shows how filter types interact in the filter pipeline.

请求通过授权过滤器、资源过滤器、模型绑定、操作过滤器、操作执行和操作结果转换、异常过滤器、结果过滤器和结果执行进行处理。

实现Implementation

通过不同的接口定义,筛选器同时支持同步和异步实现。Filters support both synchronous and asynchronous implementations through different interface definitions.

同步筛选器可以在其管道阶段之前 (On-Stage-Executing) 和之后 (On-Stage-Executed) 运行代码。Synchronous filters can run code before (On-Stage-Executing) and after (On-Stage-Executed) their pipeline stage. 例如,OnActionExecuting 在调用操作方法之前调用。For example, OnActionExecuting is called before the action method is called. OnActionExecuted 在操作方法返回之后调用。OnActionExecuted is called after the action method returns.

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 方法:Asynchronous filters define an On-Stage-ExecutionAsync method:

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

在前面的代码中,SampleAsyncActionFilter 具有执行操作方法的 ActionExecutionDelegate (next)。In the preceding code, the SampleAsyncActionFilter has an ActionExecutionDelegate (next) that executes the action method. 每个 On-Stage-ExecutionAsync 方法采用执行筛选器的管道阶段的 FilterType-ExecutionDelegateEach of the On-Stage-ExecutionAsync methods take a FilterType-ExecutionDelegate that executes the filter's pipeline stage.

多个筛选器阶段Multiple filter stages

可以在单个类中实现多个筛选器阶段的接口。Interfaces for multiple filter stages can be implemented in a single class. 例如,ActionFilterAttribute 类实现 IActionFilterIResultFilter 及其异步等效接口。For example, the ActionFilterAttribute class implements IActionFilter, IResultFilter, and their async equivalents.

筛选器接口的同步和异步版本任意实现一个,而不是同时实现 。Implement either the synchronous or the async version of a filter interface, not both. 运行时会先查看筛选器是否实现了异步接口,如果是,则调用该接口。The runtime 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 asynchronous and synchronous interfaces are implemented in one class, only the async method is called. 使用抽象类时(如 ActionFilterAttribute),将为每种筛选器类型仅重写同步方法或仅重写异步方法。When using abstract classes like ActionFilterAttribute override only the synchronous methods or the async method for each filter type.

内置筛选器属性Built-in filter attributes

ASP.NET Core 包含许多可子类化和自定义的基于属性的内置筛选器。ASP.NET Core includes built-in attribute-based filters that can be subclassed and customized. 例如,以下结果筛选器会向响应添加标头:For example, the following result filter adds a header to the response:

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

通过使用属性,筛选器可接收参数,如前面的示例所示。Attributes allow filters to accept arguments, as shown in the preceding example. AddHeaderAttribute 添加到控制器或操作方法,并指定 HTTP 标头的名称和值:Apply the AddHeaderAttribute to a controller or action method and specify the name and value of the HTTP header:

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

多种筛选器接口具有相应属性,这些属性可用作自定义实现的基类。Several of the filter interfaces have corresponding attributes that can be used as base classes for custom implementations.

筛选器属性:Filter attributes:

筛选器作用域和执行顺序Filter scopes and order of execution

可以将筛选器添加到管道中的三个作用域 之一:A filter can be added to the pipeline at one of three scopes:

  • 在操作上使用属性。Using an attribute on an action.
  • 在控制器上使用属性。Using an attribute on a controller.
  • 所有控制器和操作的全局筛选器,如下面的代码所示:Globally for all controllers and actions as shown in the following code:
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 集合全局添加三个筛选器。The preceding code adds three filters globally using the MvcOptions.Filters collection.

默认执行顺序Default order of execution

当管道的某个特定阶段有多个筛选器时,作用域可确定筛选器执行的默认顺序。When there are multiple filters for a particular stage of the pipeline, scope determines the default order of filter execution. 全局筛选器涵盖类筛选器,类筛选器又涵盖方法筛选器。Global filters surround class filters, which in turn surround method filters.

在筛选器嵌套模式下,筛选器的 after 代码会按照与 before 代码相反的顺序运行。As a result of filter nesting, the after code of filters runs in the reverse order of the before code. 筛选器序列:The filter sequence:

  • 全局筛选器的 before 代码。The before code of global filters.
    • 控制器筛选器的 before 代码。The before code of controller filters.
      • 操作方法筛选器的 before 代码。The before code of action method filters.
      • 操作方法筛选器的 after 代码。The after code of action method filters.
    • 控制器筛选器的 after 代码。The after code of controller filters.
  • 全局筛选器的 after 代码。The after code of global filters.

下面的示例阐释了为同步操作筛选器调用筛选器方法的顺序。The following example that illustrates the order in which filter methods are called for synchronous action filters.

序列Sequence 筛选器作用域Filter scope 筛选器方法Filter method
11 GlobalGlobal OnActionExecuting
22 控制器Controller OnActionExecuting
33 方法Method OnActionExecuting
44 方法Method OnActionExecuted
55 控制器Controller OnActionExecuted
66 GlobalGlobal OnActionExecuted

此序列显示:This sequence shows:

  • 方法筛选器已嵌套在控制器筛选器中。The method filter is nested within the controller filter.
  • 控制器筛选器已嵌套在全局筛选器中。The controller filter is nested within the global filter.

控制器和 Razor 页面级筛选器Controller and Razor Page level filters

继承自 Controller 基类的每个控制器包括 Controller.OnActionExecutingController.OnActionExecutionAsyncController.OnActionExecuted OnActionExecuted 方法。Every controller that inherits from the Controller base class includes Controller.OnActionExecuting, Controller.OnActionExecutionAsync, and Controller.OnActionExecuted OnActionExecuted methods. 这些方法:These methods:

  • 覆盖为给定操作运行的筛选器。Wrap the filters that run for a given action.
  • OnActionExecuting 在所有操作筛选器之前调用。OnActionExecuting is called before any of the action's filters.
  • OnActionExecuted 在所有操作筛选器之后调用。OnActionExecuted is called after all of the action filters.
  • OnActionExecutionAsync 在所有操作筛选器之前调用。OnActionExecutionAsync is called before any of the action's filters. next 之后的筛选器中的代码在操作方法之后运行。Code in the filter after next runs after the action method.

例如,在下载示例中,启动时全局应用 MySampleActionFilterFor example, in the download sample, MySampleActionFilter is applied globally in startup.

TestControllerThe TestController:

  • SampleActionFilterAttribute ([SampleActionFilter]) 应用于 FilterTest2 操作:Applies the SampleActionFilterAttribute ([SampleActionFilter]) to the FilterTest2 action:
  • 重写 OnActionExecutingOnActionExecutedOverrides OnActionExecuting and 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 运行以下代码:Navigating to https://localhost:5001/Test/FilterTest2 runs the following code:

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

对于 Razor Pages,请参阅通过重写筛选器方法实现 Razor 页面筛选器For Razor Pages, see Implement Razor Page filters by overriding filter methods.

重写默认顺序Overriding the default order

可以通过实现 IOrderedFilter 来重写默认执行序列。The default sequence of execution can be overridden by implementing IOrderedFilter. IOrderedFilter 公开了 Order 属性来确定执行顺序,该属性优先于作用域。IOrderedFilter exposes the Order property that takes precedence over scope to determine the order of execution. 具有较低的 Order 值的筛选器:A filter with a lower Order value:

  • 在具有较高的 Order 值的筛选器之前运行 before 代码。Runs the before code before that of a filter with a higher value of Order.
  • 在具有较高的 Order 值的筛选器之后运行 after 代码。Runs the after code after that of a filter with a higher Order value.

可以使用构造函数参数设置 Order 属性:The Order property can be set with a constructor parameter:

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

请考虑前面示例中所示的 3 个相同操作筛选器。Consider the same 3 action filters shown in the preceding example. 如果控制器和全局筛选器的 Order 属性分别设置为 1 和 2,则会反转执行顺序。If the Order property of the controller and global filters is set to 1 and 2 respectively, the order of execution is reversed.

序列Sequence 筛选器作用域Filter scope Order 属性Order property 筛选器方法Filter method
11 方法Method 00 OnActionExecuting
22 控制器Controller 11 OnActionExecuting
33 GlobalGlobal 22 OnActionExecuting
44 GlobalGlobal 22 OnActionExecuted
55 控制器Controller 11 OnActionExecuted
66 方法Method 00 OnActionExecuted

在确定筛选器的运行顺序时,Order 属性重写作用域。The Order property overrides scope when determining the order in which filters run. 先按顺序对筛选器排序,然后使用作用域消除并列问题。Filters are sorted first by order, then scope is used to break ties. 所有内置筛选器实现 IOrderedFilter 并将默认 Order 值设为 0。All of the built-in filters implement IOrderedFilter and set the default Order value to 0. 对于内置筛选器,作用域会确定顺序,除非将 Order 设为非零值。For built-in filters, scope determines order unless Order is set to a non-zero value.

取消和设置短路Cancellation and short-circuiting

通过设置提供给筛选器方法的 ResourceExecutingContext 参数上的 Result 属性,可以使筛选器管道短路。The filter pipeline can be short-circuited by setting the Result property on the ResourceExecutingContext parameter provided to the filter method. 例如,以下资源筛选器将阻止执行管道的其余阶段:For instance, the following Resource filter prevents the rest of the pipeline from executing:

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

在下面的代码中,ShortCircuitingResourceFilterAddHeader 筛选器都以 SomeResource 操作方法为目标。In the following code, both the ShortCircuitingResourceFilter and the AddHeader filter target the SomeResource action method. ShortCircuitingResourceFilterThe ShortCircuitingResourceFilter:

  • 先运行,因为它是资源筛选器且 AddHeader 是操作筛选器。Runs first, because it's a Resource Filter and AddHeader is an Action Filter.
  • 对管道的其余部分进行短路处理。Short-circuits the rest of the pipeline.

这样 AddHeader 筛选器就不会为 SomeResource 操作运行。Therefore the AddHeader filter never runs for the SomeResource action. 如果这两个筛选器都应用于操作方法级别,只要 ShortCircuitingResourceFilter 先运行,此行为就不会变。This behavior would be the same if both filters were applied at the action method level, provided the ShortCircuitingResourceFilter ran first. 先运行 ShortCircuitingResourceFilter(考虑到它的筛选器类型),或显式使用 Order 属性。The ShortCircuitingResourceFilter runs first because of its filter type, or by explicit use of Order property.

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

依赖关系注入Dependency injection

可按类型或实例添加筛选器。Filters can be added by type or by instance. 如果添加实例,该实例将用于每个请求。If an instance is added, that instance is used for every request. 如果添加类型,则将激活该类型。If a type is added, it's type-activated. 激活类型的筛选器意味着:A type-activated filter means:

  • 将为每个请求创建一个实例。An instance is created for each request.
  • 依赖关系注入 (DI) 将填充所有构造函数依赖项。Any constructor dependencies are populated by dependency injection (DI).

如果将筛选器作为属性实现并直接添加到控制器类或操作方法中,则该筛选器不能由依赖关系注入 (DI) 提供构造函数依赖项。Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). 无法由 DI 提供构造函数依赖项,因为:Constructor dependencies cannot be provided by DI because:

  • 属性在应用时必须提供自己的构造函数参数。Attributes must have their constructor parameters supplied where they're applied.
  • 这是属性工作原理上的限制。This is a limitation of how attributes work.

以下筛选器支持从 DI 提供的构造函数依赖项:The following filters support constructor dependencies provided from DI:

可以将前面的筛选器应用于控制器或操作方法:The preceding filters can be applied to a controller or action method:

可以从 DI 获取记录器。Loggers are available from DI. 但是,避免创建和使用筛选器仅用于日志记录。However, avoid creating and using filters purely for logging purposes. 内置框架日志记录通常提供日志记录所需的内容。The built-in framework logging typically provides what's needed for logging. 添加到筛选器的日志记录:Logging added to filters:

  • 应重点关注业务域问题或特定于筛选器的行为。Should focus on business domain concerns or behavior specific to the filter.
  • 不应记录操作或其他框架事件 。Should not log actions or other framework events. 内置筛选器记录操作和框架事件。The built in filters log actions and framework events.

ServiceFilterAttributeServiceFilterAttribute

ConfigureServices 中注册服务筛选器实现类型。Service filter implementation types are registered in ConfigureServices. ServiceFilterAttribute 可从 DI 检索筛选器实例。A ServiceFilterAttribute retrieves an instance of the filter from DI.

以下代码显示 AddHeaderResultServiceFilterThe following code shows the 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}");
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
    }
}

在以下代码中,AddHeaderResultServiceFilter 将添加到 DI 容器中:In the following code, AddHeaderResultServiceFilter is added to the DI container:

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 筛选器的实例:In the following code, the ServiceFilter attribute retrieves an instance of the AddHeaderResultServiceFilter filter from DI:

[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
    return View();
}

使用 ServiceFilterAttribute 时,ServiceFilterAttribute.IsReusable 设置:When using ServiceFilterAttribute, setting ServiceFilterAttribute.IsReusable:

  • 提供以下提示:筛选器实例可能在其创建的请求范围之外被重用。 Provides a hint that the filter instance may be reused outside of the request scope it was created within. ASP.NET Core 运行时不保证:The ASP.NET Core runtime doesn't guarantee:

    • 将创建筛选器的单一实例。That a single instance of the filter will be created.
    • 稍后不会从 DI 容器重新请求筛选器。The filter will not be re-requested from the DI container at some later point.
  • 不应与依赖于生命周期不同于单一实例的服务的筛选器一起使用。Should not be used with a filter that depends on services with a lifetime other than singleton.

ServiceFilterAttribute 可实现 IFilterFactoryServiceFilterAttribute implements IFilterFactory. IFilterFactory 公开用于创建 IFilterMetadata 实例的 CreateInstance 方法。IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance 从 DI 中加载指定的类型。CreateInstance loads the specified type from DI.

TypeFilterAttributeTypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute 类似,但不会直接从 DI 容器解析其类型。TypeFilterAttribute is similar to ServiceFilterAttribute, but its type isn't resolved directly from the DI container. 它使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 对类型进行实例化。It instantiates the type by using Microsoft.Extensions.DependencyInjection.ObjectFactory.

因为不会直接从 DI 容器解析 TypeFilterAttribute 类型:Because TypeFilterAttribute types aren't resolved directly from the DI container:

  • 使用 TypeFilterAttribute 引用的类型不需要注册在 DI 容器中。Types that are referenced using the TypeFilterAttribute don't need to be registered with the DI container. 它们具备由 DI 容器实现的依赖项。They do have their dependencies fulfilled by the DI container.
  • TypeFilterAttribute 可以选择为类型接受构造函数参数。TypeFilterAttribute can optionally accept constructor arguments for the type.

使用 TypeFilterAttribute 时,TypeFilterAttribute.IsReusable 设置:When using TypeFilterAttribute, setting TypeFilterAttribute.IsReusable:

  • 提供提示:筛选器实例可能在其创建的请求范围之外被重用。 Provides hint that the filter instance may be reused outside of the request scope it was created within. ASP.NET Core 运行时不保证将创建筛选器的单一实例。The ASP.NET Core runtime provides no guarantees that a single instance of the filter will be created.

  • 不应与依赖于生命周期不同于单一实例的服务的筛选器一起使用。Should not be used with a filter that depends on services with a lifetime other than singleton.

下面的示例演示如何使用 TypeFilterAttribute 将参数传递到类型:The following example shows how to pass arguments to a type using 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)
    { }
}

授权筛选器Authorization filters

授权筛选器:Authorization filters:

  • 是筛选器管道中运行的第一个筛选器。Are the first filters run in the filter pipeline.
  • 控制对操作方法的访问。Control access to action methods.
  • 具有在它之前的执行的方法,但没有之后执行的方法。Have a before method, but no after method.

自定义授权筛选器需要自定义授权框架。Custom authorization filters require a custom authorization framework. 建议配置授权策略或编写自定义授权策略,而不是编写自定义筛选器。Prefer configuring the authorization policies or writing a custom authorization policy over writing a custom filter. 内置授权筛选器:The built-in authorization filter:

  • 调用授权系统。Calls the authorization system.
  • 不授权请求。Does not authorize requests.

不会在授权筛选器中引发异常 :Do not throw exceptions within authorization filters:

  • 不会处理异常。The exception will not be handled.
  • 异常筛选器不会处理异常。Exception filters will not handle the exception.

在授权筛选器出现异常时请小心应对。Consider issuing a challenge when an exception occurs in an authorization filter.

详细了解授权Learn more about Authorization.

资源筛选器Resource filters

资源筛选器:Resource filters:

如果要使大部分管道短路,资源筛选器会很有用。Resource filters are useful to short-circuit most of the pipeline. 例如,如果缓存命中,则缓存筛选器可以绕开管道的其余阶段。For example, a caching filter can avoid the rest of the pipeline on a cache hit.

资源筛选器示例:Resource filter examples:

操作筛选器Action filters

重要

操作筛选器不 应用于 Razor Pages。Action filters do not apply to Razor Pages. Razor Pages 支持 IPageFilterIAsyncPageFilterRazor Pages supports IPageFilter and IAsyncPageFilter . 有关详细信息,请参阅 Razor 页面的筛选方法For more information, see Filter methods for Razor Pages.

操作筛选器:Action filters:

以下代码显示示例操作筛选器:The following code shows a sample action filter:

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 提供以下属性:The ActionExecutingContext provides the following properties:

  • ActionArguments - 用于读取操作方法的输入。ActionArguments - enables the inputs to an action method be read.
  • Controller - 用于处理控制器实例。Controller - enables manipulating the controller instance.
  • Result - 设置 Result 会使操作方法和后续操作筛选器的执行短路。Result - setting Result short-circuits execution of the action method and subsequent action filters.

在操作方法中引发异常:Throwing an exception in an action method:

  • 防止运行后续筛选器。Prevents running of subsequent filters.
  • 与设置 Result 不同,结果被视为失败而不是成功。Unlike setting Result, is treated as a failure instead of a successful result.

ActionExecutedContext 提供 ControllerResult 以及以下属性:The ActionExecutedContext provides Controller and Result plus the following properties:

  • Canceled - 如果操作执行已被另一个筛选器设置短路,则为 true。Canceled - True if the action execution was short-circuited by another filter.

  • Exception - 如果操作或之前运行的操作筛选器引发了异常,则为非 NULL 值。Exception - Non-null if the action or a previously run action filter threw an exception. 将此属性设置为 null:Setting this property to null:

    • 有效地处理异常。Effectively handles the exception.
    • 执行 Result,从操作方法中将它返回。Result is executed as if it was returned from the action method.

对于 IAsyncActionFilter,一个向 ActionExecutionDelegate 的调用可以达到以下目的:For an IAsyncActionFilter, a call to the ActionExecutionDelegate:

  • 执行所有后续操作筛选器和操作方法。Executes any subsequent action filters and the action method.
  • 返回 ActionExecutedContextReturns ActionExecutedContext.

若要设置短路,可将 Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result 分配到某个结果实例,并且不调用 next (ActionExecutionDelegate)。To short-circuit, assign Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result to a result instance and don't call next (the ActionExecutionDelegate).

该框架提供一个可子类化的抽象 ActionFilterAttributeThe framework provides an abstract ActionFilterAttribute that can be subclassed.

OnActionExecuting 操作筛选器可用于:The OnActionExecuting action filter can be used to:

  • 验证模型状态。Validate model state.
  • 如果状态无效,则返回错误。Return an error if the state is invalid.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }

OnActionExecuted 方法在操作方法之后运行:The OnActionExecuted method runs after the action method:

  • 可通过 Result 属性查看和处理操作结果。And can see and manipulate the results of the action through the Result property.

  • 如果操作执行已被另一个筛选器设置短路,则 Canceled 设置为 true。Canceled is set to true if the action execution was short-circuited by another filter.

  • 如果操作或后续操作筛选器引发了异常,则 Exception 设置为非 NULL 值。Exception is set to a non-null value if the action or a subsequent action filter threw an exception. Exception 设置为 null:Setting Exception to null:

    • 有效地处理异常。Effectively handles an exception.
    • 执行 ActionExecutedContext.Result,从操作方法中将它正常返回。ActionExecutedContext.Result is executed as if it were returned normally from the action method.
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);
    }
}

异常筛选器Exception filters

异常筛选器:Exception filters:

下面的异常筛选器示例使用自定义错误视图,显示在开发应用时发生的异常的相关详细信息:The following sample exception filter uses a custom error view to display details about exceptions that occur when the app is in development:

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    private readonly IHostingEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilterAttribute(
        IHostingEnvironment hostingEnvironment,
        IModelMetadataProvider modelMetadataProvider)
    {
        _hostingEnvironment = hostingEnvironment;
        _modelMetadataProvider = modelMetadataProvider;
    }

    public override 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;
    }
}

异常筛选器:Exception filters:

  • 没有之前和之后的事件。Don't have before and after events.
  • 实现 OnExceptionOnExceptionAsyncImplement OnException or OnExceptionAsync.
  • 处理 Razor 页面或控制器创建、模型绑定、操作筛选器或操作方法中发生的未经处理的异常。Handle unhandled exceptions that occur in Razor Page or controller creation, model binding, action filters, or action methods.
  • 请不要捕获资源筛选器、结果筛选器或 MVC 结果执行中发生的异常 。Do not catch exceptions that occur in resource filters, result filters, or MVC result execution.

若要处理异常,请将 ExceptionHandled 属性设置为 true,或编写响应。To handle an exception, set the ExceptionHandled property to true or write a response. 这将停止传播异常。This stops propagation of the exception. 异常筛选器无法将异常转变为“成功”。An exception filter can't turn an exception into a "success". 只有操作筛选器才能执行该转变。Only an action filter can do that.

异常筛选器:Exception filters:

  • 非常适合捕获发生在操作中的异常。Are good for trapping exceptions that occur within actions.
  • 并不像错误处理中间件那么灵活。Are not as flexible as error handling middleware.

建议使用中间件处理异常。Prefer middleware for exception handling. 基于所调用的操作方法,仅当错误处理不同时,才使用异常筛选器 。Use exception filters only where error handling differs based on which action method is called. 例如,应用可能具有用于 API 终结点和视图/HTML 的操作方法。For example, an app might have action methods for both API endpoints and for views/HTML. API 终结点可能返回 JSON 形式的错误信息,而基于视图的操作可能返回 HTML 形式的错误页。The API endpoints could return error information as JSON, while the view-based actions could return an error page as HTML.

结果筛选器Result filters

结果筛选器:Result filters:

IResultFilter 和 IAsyncResultFilterIResultFilter and IAsyncResultFilter

以下代码显示一个添加 HTTP 标头的结果筛选器:The following code shows a result filter that adds an HTTP header:

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

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
    }
}

要执行的结果类型取决于所执行的操作。The kind of result being executed depends on the action. 返回视图的操作会将所有 Razor 处理作为要执行的 ViewResult 的一部分。An action returning a view would include all razor processing as part of the ViewResult being executed. API 方法可能会将某些序列化操作作为结果执行的一部分。An API method might perform some serialization as part of the execution of the result. 详细了解操作结果Learn more about action results

当操作或操作筛选器生成操作结果时,仅针对成功的结果执行结果筛选器。Result filters are only executed for successful results - when the action or action filters produce an action result. 当异常筛选器处理异常时,不执行结果筛选器。Result filters are not executed when exception filters handle an exception.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 方法可以将 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel 设置为 true,使操作结果和后续结果筛选器的执行短路。The Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting method can short-circuit execution of the action result and subsequent result filters by setting Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel to true. 设置短路时写入响应对象,以免生成空响应。Write to the response object when short-circuiting to avoid generating an empty response. 如果在 IResultFilter.OnResultExecuting 中引发异常,则会导致:Throwing an exception in IResultFilter.OnResultExecuting will:

  • 阻止操作结果和后续筛选器的执行。Prevent execution of the action result and subsequent filters.
  • 结果被视为失败而不是成功。Be treated as a failure instead of a successful result.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 方法运行时:When the Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted method runs:

  • 响应可能已发送到客户端,且无法更改。The response has likely been sent to the client and cannot be changed.
  • 如果引发了异常,则不会发送响应正文。If an exception was thrown, the response body is not sent.

如果操作结果执行已被另一个筛选器设置短路,则 ResultExecutedContext.Canceled 设置为 trueResultExecutedContext.Canceled is set to true if the action result execution was short-circuited by another filter.

如果操作结果或后续结果筛选器引发了异常,则 ResultExecutedContext.Exception 设置为非 NULL 值。ResultExecutedContext.Exception is set to a non-null value if the action result or a subsequent result filter threw an exception. Exception 设置为 NULL 可有效地处理异常,并防止 ASP.NET Core 在管道的后续阶段重新引发该异常。Setting Exception to null effectively handles an exception and prevents the exception from being rethrown by ASP.NET Core later in the pipeline. 处理结果筛选器中出现的异常时,没有可靠的方法来将数据写入响应。There is no reliable way to write data to a response when handling an exception in a result filter. 如果在操作结果引发异常时标头已刷新到客户端,则没有任何可靠的机制可用于发送失败代码。If the headers have been flushed to the client when an action result throws an exception, there's no reliable mechanism to send a failure code.

对于 IAsyncResultFilter,通过调用 ResultExecutionDelegate 上的 await next 可执行所有后续结果筛选器和操作结果。For an IAsyncResultFilter, a call to await next on the ResultExecutionDelegate executes any subsequent result filters and the action result. 若要设置短路,请将 ResultExecutingContext.Cancel 设置为 true,并且不调用 ResultExecutionDelegateTo short-circuit, set ResultExecutingContext.Cancel to true and don't call the ResultExecutionDelegate:

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

    }
}

该框架提供一个可子类化的抽象 ResultFilterAttributeThe framework provides an abstract ResultFilterAttribute that can be subclassed. 前面所示的 AddHeaderAttribute 类是一种结果筛选器属性。The AddHeaderAttribute class shown previously is an example of a result filter attribute.

IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilterIAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter

IAlwaysRunResultFilterIAsyncAlwaysRunResultFilter 接口声明了一个针对所有操作结果运行的 IResultFilter 实现。The IAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter interfaces declare an IResultFilter implementation that runs for all action results. 筛选器将应用于所有操作结果,除非:The filter is applied to all action results unless:

IExceptionFilterIAuthorizationFilter 之外的筛选器不会使 IAlwaysRunResultFilterIAsyncAlwaysRunResultFilter 短路。Filters other than IExceptionFilter and IAuthorizationFilter don't short-circuit IAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter.

例如,以下筛选器始终运行并在内容协商失败时设置具有“422 无法处理的实体” 状态代码的操作结果 (ObjectResult):For example, the following filter always runs and sets an action result (ObjectResult) with a 422 Unprocessable Entity status code when content negotiation fails:

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

IFilterFactoryIFilterFactory

IFilterFactory 可实现 IFilterMetadataIFilterFactory implements IFilterMetadata. 因此,IFilterFactory 实例可在筛选器管道中的任意位置用作 IFilterMetadata 实例。Therefore, an IFilterFactory instance can be used as an IFilterMetadata instance anywhere in the filter pipeline. 当运行时准备调用筛选器时,它会尝试将其转换为 IFilterFactoryWhen the runtime prepares to invoke the filter, it attempts to cast it to an IFilterFactory. 如果转换成功,则调用 CreateInstance 方法来创建将调用的 IFilterMetadata 实例。If that cast succeeds, the CreateInstance method is called to create the IFilterMetadata instance that is invoked. 这提供了一种很灵活的设计,因为无需在应用启动时显式设置精确的筛选器管道。This provides a flexible design, since the precise filter pipeline doesn't need to be set explicitly when the app starts.

可以使用自定义属性实现来实现 IFilterFactory 作为另一种创建筛选器的方法:IFilterFactory can be implemented using custom attribute implementations as another approach to creating filters:

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

可以通过运行下载示例来测试前面的代码:The preceding code can be tested by running the download sample:

  • 调用 F12 开发人员工具。Invoke the F12 developer tools.
  • 导航到 https://localhost:5001/Sample/HeaderWithFactoryNavigate to https://localhost:5001/Sample/HeaderWithFactory

F12 开发人员工具显示示例代码添加的以下响应标头:The F12 developer tools display the following response headers added by the sample code:

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

前面的代码创建 internal: My header 响应标头。The preceding code creates the internal: My header response header.

在属性上实现 IFilterFactoryIFilterFactory implemented on an attribute

实现 IFilterFactory 的筛选器可用于以下筛选器:Filters that implement IFilterFactory are useful for filters that:

  • 不需要传递参数。Don't require passing parameters.
  • 具备需要由 DI 填充的构造函数依赖项。Have constructor dependencies that need to be filled by DI.

TypeFilterAttribute 可实现 IFilterFactoryTypeFilterAttribute implements IFilterFactory. IFilterFactory 公开用于创建 IFilterMetadata 实例的 CreateInstance 方法。IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance 从服务容器 (DI) 中加载指定的类型。CreateInstance loads the specified type from the services container (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] 的三种方法:The following code shows three approaches to applying the [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 的首选方法。In the preceding code, decorating the method with [SampleActionFilter] is the preferred approach to applying the SampleActionFilter.

在筛选器管道中使用中间件Using middleware in the filter pipeline

资源筛选器的工作方式与中间件类似,即涵盖管道中的所有后续执行。Resource filters work like middleware in that they surround the execution of everything that comes later in the pipeline. 但筛选器又不同于中间件,它们是 ASP.NET Core 运行时的一部分,这意味着它们有权访问 ASP.NET Core 上下文和构造。But filters differ from middleware in that they're part of the ASP.NET Core runtime, which means that they have access to ASP.NET Core context and constructs.

若要将中间件用作筛选器,可创建一个具有 Configure 方法的类型,该方法可指定要注入到筛选器管道的中间件。To use middleware as a filter, create a type with a Configure method that specifies the middleware to inject into the filter pipeline. 下面的示例使用本地化中间件为请求建立当前区域性:The following example uses the localization middleware to establish the current culture for a request:

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 运行中间件:Use the MiddlewareFilterAttribute to run the middleware:

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content($"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

中间件筛选器与资源筛选器在筛选器管道的相同阶段运行,即,在模型绑定之前以及管道的其余阶段之后。Middleware filters run at the same stage of the filter pipeline as Resource filters, before model binding and after the rest of the pipeline.

后续操作Next actions