ASP.NET Core 中的篩選條件Filters in ASP.NET Core

作者:Kirk Larkin (英文)、Rick Anderson (英文)、Tom Dykstra (英文) 及 Steve Smith (英文)By 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. 例如,系統會在呼叫動作方法之前呼叫 OnActionExecutingFor example, OnActionExecuting is called before the action method is called. 系統會在傳回動作方法之後呼叫 OnActionExecutedOnActionExecuted 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.

因為篩選條件巢狀結構的原因,篩選條件的「之後」程式碼的執行順序會與「之前」程式碼相反。As a result of filter nesting, the after code of filters runs in the reverse order of the before code. 篩選條件序列:The filter sequence:

  • 全域篩選條件的「之前」程式碼。The before code of global filters.
    • 控制器篩選條件的「之前」程式碼。The before code of controller filters.
      • 動作方法篩選條件的「之前」程式碼。The before code of action method filters.
      • 動作方法篩選條件的「之後」程式碼。The after code of action method filters.
    • 控制器篩選條件的「之後」程式碼。The after code of controller filters.
  • 全域篩選條件的「之後」程式碼。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.OnActionExecutionAsync,以及 Controller.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.
  • 系統會在呼叫任何動作的篩選條件之前呼叫 OnActionExecutingOnActionExecuting is called before any of the action's filters.
  • 系統會在呼叫所有動作篩選條件之後呼叫 OnActionExecutedOnActionExecuted is called after all of the action filters.
  • 系統會在呼叫任何動作的篩選條件之前呼叫 OnActionExecutionAsyncOnActionExecutionAsync is called before any of the action's filters. 在動作方法之後執行 next 後,位於篩選條件中的程式碼。Code in the filter after next runs after the action method.

例如,在下載範例中,MySampleActionFilter 會在啟動時全域套用。For 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 值的篩選條件之前執行「之前」程式碼。Runs the before code before that of a filter with a higher value of Order.
  • 在具有較高 Order 值的篩選條件之後執行「之後」程式碼。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.

因此,SomeResource 動作的 AddHeader 篩選條件永遠不會執行。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.IsReusableWhen 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 會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance 會從 DI 載入指定的類型。CreateInstance loads the specified type from DI.

TypeFilterAttributeTypeFilterAttribute

TypeFilterAttribute 類似於 ServiceFilterAttribute,但其類型不會直接從 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.

由於 TypeFilterAttribute 類型不會直接從 DI 容器解析: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.IsReusableWhen 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).

架構提供抽象 ActionFilterAttribute,其可被子類別化。The 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. 傳回檢視的動作會在執行中的 ViewResult 裡包含處理中的所有 Razor。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.ResultExecutingContext.Cancel 設為 trueMicrosoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 方法可以縮短動作結果和後續結果篩選條件的執行。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;
        }

    }
}

架構提供抽象 ResultFilterAttribute,其可被子類別化。The 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 以外的篩選條件並不會縮短 IAlwaysRunResultFilterIAsyncAlwaysRunResultFilterFilters other than IExceptionFilter and IAuthorizationFilter don't short-circuit IAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter.

例如,下列篩選一律會執行,並會在內容交涉失敗時,為動作結果 (ObjectResult) 設定「422 無法處理的實體」狀態碼: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.

可以使用自訂屬性實作作為另一種建立篩選條件的方法,來實作 IFilterFactoryIFilterFactory 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 會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。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