作者:Kirk Larkin \(英文\)、Rick Anderson \(英文\)、Tom Dykstra \(英文\) 及 Steve Smith \(英文\)
ASP.NET Core 中的篩選條件可讓程式碼在要求處理管線中的特定階段之前或之後執行。
內建篩選器處理的工作包括:
- 授權,避免存取使用者未獲授權的資源。
- 回應快取,縮短要求管線,傳回快取的回應。
可以建立自訂篩選條件來處理跨領域關注。 跨領域關注的範例包括錯誤處理、快取、設定、授權及記錄。 篩選能避免重複的程式碼。 例如,錯誤處理的例外篩選器可以統一處理錯誤。
本文件適用於 Razor Pages、API 控制器,以及具有檢視的控制器。 篩選無法直接與 Razor 元件搭配使用。 篩選條件只能在以下情況下間接影響元件:
- 元件內嵌在頁面或檢視中。
- 頁面或控制器和檢視會使用篩選。
篩選條件如何運作
篩選條件會在「ASP.NET Core 動作引動過程管線」中執行,其有時也被稱為「篩選條件管線」。 ASP.NET Core 選取完要執行的動作後,會執行篩選條件管線:
篩選類型
每種過濾器類型會在過濾器管道的不同階段執行:
-
- 先跑。
- 判斷使用者是否獲得此要求的授權。
- 如果請求未獲授權,則中斷管道。
-
- 會在授權之後執行。
-
OnResourceExecuting 在篩選管線的其餘部分之前執行程式碼。 例如,
OnResourceExecuting
會在模型繫結之前執行程式碼。 - OnResourceExecuted 在整個管線完成後執行程式碼。
-
- 執行於呼叫動作方法之前和之後。
- 可以變更傳遞至動作的引數。
- 可以變更從動作傳回的結果。
- 不支援在Razor Pages 中。
端點篩選:
- 執行於呼叫動作方法之前和之後。
- 可以變更傳遞至動作的引數。
- 可以變更從動作傳回的結果。
- 不支援在Razor Pages 中。
- 可以在動作和以路由處理常式為基礎的端點上叫用。
-
- 將全域原則套用至未處理的例外狀況,這些例外狀況會在寫入回應本文之前發生。
- 在模型系結和動作篩選之後執行,但在執行動作結果之前執行。
- 只有在動作執行或動作結果執行期間發生未處理的例外狀況時,才會執行 。
- 請勿針對中間件執行、路由或模型系結期間擲回的例外狀況執行。
-
- 在執行動作結果之前和之後立即執行。
- 只有當動作方法成功執行時,才會執行。
- 適用於必須包圍檢視或格式器執行的邏輯。
Razor 頁面也支援頁面篩選,此篩選會在頁面處理常式之前和之後執行。
實施
篩選條件同時支援透過不同介面定義的同步和非同步實作。
同步篩選可以在其管線階段之前和之後執行。 例如,系統會在呼叫動作方法之前呼叫 OnActionExecuting。 系統會在傳回動作方法之後呼叫 OnActionExecuted:
public class SampleActionFilter : 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
方法。 例如 OnActionExecutionAsync:
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
// Do something before the action executes.
await next();
// Do something after the action executes.
}
}
在上述程式碼中,SampleAsyncActionFilter
具有會執行動作方法的 ActionExecutionDelegate (next
)。
多個篩選條件階段
可以在單一類別中實作多個篩選條件階段的介面。 例如,ActionFilterAttribute 類別會實作:
請實作同步或非同步版本的篩選介面,但避免同時實作兩者。 執行階段會先檢查以查看篩選條件是否會實作非同步介面,如果是,便會呼叫該介面。 如果沒有,它會呼叫同步介面的一個或多個方法。 如果同時在單一類別中實作非同步和同步介面,系統只會呼叫非同步方法。 使用像 ActionFilterAttribute 這樣的抽象類別時,請僅覆寫每個篩選器類型的同步方法或非同步方法。
內建篩選條件屬性
ASP.NET Core 包含內建的屬性型篩選條件,可對其進行子類別化和自訂。 例如,下列結果篩選條件會將標頭新增至回應:
public class ResponseHeaderAttribute : ActionFilterAttribute
{
private readonly string _name;
private readonly string _value;
public ResponseHeaderAttribute(string name, string value) =>
(_name, _value) = (name, value);
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(_name, _value);
base.OnResultExecuting(context);
}
}
屬性可讓篩選條件接受引數,如上述範例所示。 將 ResponseHeaderAttribute
新增至控制器或動作方法,並指定 HTTP 標頭的名稱和值:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
使用瀏覽器開發人員工具之類的工具來檢查標頭。
回應標頭底下會顯示 filter-header: Filter Value
。
下列程式碼會同時將 ResponseHeaderAttribute
套用到控制器和動作:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
[ResponseHeader("Another-Filter-Header", "Another Filter Value")]
public IActionResult Multiple() =>
Content("Examine the response headers using the F12 developer tools.");
}
Multiple
動作的回應會包括下列標頭:
filter-header: Filter Value
another-filter-header: Another Filter Value
有幾個篩選條件介面有對應的屬性,可用來作為自訂實作的基底類別。
篩選條件屬性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
篩選條件無法套用至 Razor 頁面處理方法。 可以套用至 Razor Page 模型或套用至全域。
篩選條件範圍和執行的順序
篩選條件可以在三種「範圍」之一被添加到管線中。
- 針對控制器或 Razor 頁面使用屬性。
- 在控制器的動作上使用屬性。 篩選屬性無法套用至 Razor Pages 處理常式方法。
- 可全域套用至所有控制器、動作和頁面,如下列程式碼所示:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
預設執行順序
當管線的特定階段有多個篩選條件時,範圍會決定篩選條件的預設執行順序。 全域篩選條件會圍繞類別篩選條件,後者又圍繞方法篩選條件。
因為篩選條件巢狀結構的原因,篩選條件的「之後」程式碼的執行順序會與「之前」程式碼相反。 篩選條件序列:
- 全域篩選條件之前的程式碼。
- 控制器篩選條件的「之前」程式碼。
- 動作方法篩選條件的「之前」程式碼。
- 在動作方法篩選器之後的程式碼。
- 控制器篩選條件的「之後」程式碼。
- 控制器篩選條件的「之前」程式碼。
- 全域篩選條件的後置程式碼。
下列範例說明針對同步動作篩選執行篩選方法的順序:
序列 | 篩選條件範圍 | 過濾方法 |
---|---|---|
1 | 全球 | OnActionExecuting |
2 | 控制器 | OnActionExecuting |
3 | 動作 | OnActionExecuting |
4 | 動作 | OnActionExecuted |
5 | 控制器 | OnActionExecuted |
6 | 全球 | OnActionExecuted |
控制器層級篩選
繼承自 Controller 的每個控制器都包含 OnActionExecuting、OnActionExecutionAsync 和 OnActionExecuted 方法。 這些方法會包裝針對指定動作執行的篩選條件:
-
OnActionExecuting
會在任何動作的篩選之前執行。 -
OnActionExecuted
會在動作的所有篩選之後執行。 -
OnActionExecutionAsync
會在任何動作的篩選之前執行。 呼叫next
之後的程式碼會在動作的篩選之後執行。
下列 ControllerFiltersController
類別:
- 將
SampleActionFilterAttribute
([SampleActionFilter]
) 套用至控制器。 - 覆寫
OnActionExecuting
和OnActionExecuted
。
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");
base.OnActionExecuted(context);
}
public IActionResult Index()
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(Index)}");
return Content("Check the Console.");
}
}
瀏覽至 https://localhost:<port>/ControllerFilters
執行下列程式碼:
ControllerFiltersController.OnActionExecuting
GlobalSampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
ControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
控制器層級篩選會將 Order 屬性設定為 int.MinValue
。 控制器層級篩選無法設定為在套用至方法的篩選之後執行。 下一節會說明順序。
針對 Razor 頁面,請參閱覆寫篩選方法以實作 Razor 頁面篩選。
覆寫預設順序
可以藉由實作 IOrderedFilter 來覆寫預設執行序列。
IOrderedFilter
會公開 Order 屬性,其優先順序會高於範圍以決定執行順序。 具有較低 Order
值的篩選條件:
- 在具有較高 值的篩選條件之前執行「之前」
Order
程式碼。 - 在具有較高 值的篩選條件之後執行「之後」
Order
程式碼。
在控制器層級篩選範例中,GlobalSampleActionFilter
具有全域範圍,因此會在具有控制器範圍的 SampleActionFilterAttribute
之前執行。 若要先執行 SampleActionFilterAttribute
,請將其順序設定為 int.MinValue
:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
若要先執行全域篩選 GlobalSampleActionFilter
,請將其 Order
設定為 int.MinValue
:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
取消和短路
可以藉由設定提供給過濾器方法之 Result 參數上的 ResourceExecutingContext 屬性來短路過濾器管線。 例如,下列資源過濾器會阻止管線的其餘部分執行:
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
在下列程式碼中,[ShortCircuitingResourceFilter]
和 [ResponseHeader]
篩選條件都以 Index
動作方法為目標。
ShortCircuitingResourceFilterAttribute
篩選條件:
- 最先執行,因為它是資源篩選條件,且
ResponseHeaderAttribute
是動作篩選條件。 - 中斷管線的其餘部分。
因此,ResponseHeaderAttribute
篩選條件永遠不會執行對於Index
動作。 假設 ShortCircuitingResourceFilterAttribute
先執行的話,如果這兩個篩選條件都套用在動作方法層級,此行為也會相同。
ShortCircuitingResourceFilterAttribute
會因為其篩選類型而先執行:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
依賴注入
可以依類型或實例新增篩選條件。 如果新增執行個體,該執行個體將用於每個要求。 如果新增了一種類型,它就會被啟用。 由類型啟動的篩選條件表示:
- 系統會針對每個請求建立執行個體。
- 任何建構函式的依賴項目都會由依賴注入 (DI) 所填入。
實作為屬性並直接新增至控制器類別或動作方法的篩選器,無法透過依賴注入 (DI) 提供建構函數的相依性。 建構函式相依性無法由 DI 提供,因為屬性必須在其套用位置提供其建構函式參數。
下列篩選條件支援由 DI 所提供的建構函式相依性:
上述篩選條件可以被套用至控制器或動作。
記錄器可從 DI 獲得。 不過,請避免僅針對記錄目的建立並使用篩選條件。 內建框架日誌通常便足以滿足記錄需求。 記錄已新增至篩選條件:
- 應該重點關注篩選器相關的商業領域問題或具體行為。
- 不應該記錄動作或其他架構事件。 內建過濾器已記錄動作和架構事件。
ServiceFilterAttribute
服務篩選條件實作類型會註冊在 Program.cs
中。
ServiceFilterAttribute 從 DI 獲取篩選器的實例。
下列程式碼會顯示使用 DI 的 LoggingResponseHeaderFilterService
類別:
public class LoggingResponseHeaderFilterService : IResultFilter
{
private readonly ILogger _logger;
public LoggingResponseHeaderFilterService(
ILogger<LoggingResponseHeaderFilterService> logger) =>
_logger = logger;
public void OnResultExecuting(ResultExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
context.HttpContext.Response.Headers.Add(
nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
}
public void OnResultExecuted(ResultExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
}
}
在下列程式碼中,會將 LoggingResponseHeaderFilterService
新增至 DI 容器:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
在下列程式碼中,ServiceFilter
屬性會從 DI 擷取 LoggingResponseHeaderFilterService
篩選條件的執行個體:
[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
使用 ServiceFilterAttribute
時,設定 ServiceFilterAttribute.IsReusable:
- 可暗示篩選器實例可能會在其創建的請求範圍之外被重複使用。 ASP.NET Core 執行階段並不保證:
- 將會建立篩選器的單一執行個體。
- 將不會於稍後的時間從 DI 容器重新要求篩選條件。
- 不應該與依賴非單例存活期服務的篩選器搭配使用。
ServiceFilterAttribute 實現 IFilterFactory。
IFilterFactory
會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。
CreateInstance
會從 DI 載入指定的類型。
類型篩選屬性
TypeFilterAttribute 類似於 ServiceFilterAttribute,但其類型不會直接從 DI 容器解析。 它會使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 來具現化類型。
由於 TypeFilterAttribute
類型不會直接從 DI 容器解析:
- 使用
TypeFilterAttribute
參考的類型不需要向 DI 容器註冊。 不過它們的相依性會由 DI 容器滿足。 -
TypeFilterAttribute
可以選擇性地接受類型的建構函式引數。
使用 TypeFilterAttribute
時,設定 TypeFilterAttribute.IsReusable:
提示篩選條件實例「可能」在其建立的請求範圍之外重複使用。 ASP.NET Core 執行階段不保證會建立篩選器的單一實例。
不應該搭配依賴非單例存留期服務的篩選器使用。
下列範例示範如何使用 TypeFilterAttribute
將引數傳遞至類型:
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
授權篩選條件
授權篩選條件:
- 是篩選器管道中最先執行的篩選器。
- 控制對操作方法的存取。
- 有之前的方法,但沒有之後的方法。
自訂授權篩選條件需要自訂的授權架構。 最好是設定授權原則或撰寫自訂授權原則,而不要撰寫自訂篩選條件。 內建的授權篩選條件:
- 呼叫授權系統。
- 不會授權要求。
不會在授權篩選條件內擲回例外狀況:
- 該例外狀況將不會被處理。
- 例外篩選條件將不會處理例外。
請考慮在例外狀況於授權篩選條件中發生時發出挑戰。
深入了解授權。
資源篩選條件
資源篩選條件:
- 實作 IResourceFilter 或 IAsyncResourceFilter 介面。
- 執行涵蓋了大部分的篩選流程。
- 只有授權篩選條件會在資源篩選條件之前執行。
資源篩選條件很適合用來縮短大部分的管線。 例如,當快取命中時,快取篩選器可以跳過剩餘的管線處理。
資源篩選條件範例:
先前所示的短路資源篩選器。
DisableFormValueModelBindingAttribute:
- 防止模型繫結存取表單資料。
- 用於大型檔案上傳,以避免將表單資料讀入記憶體。
動作篩選條件
動作篩選條件不會套用到 Razor Pages。 Razor Pages 支援 IPageFilter 與 IAsyncPageFilter。 如需詳細資訊,請參閱 Razor Pages 的篩選方法。
動作篩選條件:
- 實作 IActionFilter 或 IAsyncActionFilter 介面。
- 他們的執行涉及行動方法的執行。
下列程式碼會顯示範例動作篩選條件:
public class SampleActionFilter : 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
會中斷 Action 方法及後續動作篩選器的執行。
在動作方法中丟出例外狀況:
- 防止執行後續篩選條件。
- 和設定
Result
不同,會被視為失敗而非成功結果。
ActionExecutedContext 提供 Controller
與 Result
,加上下列屬性:
- Canceled - 如果動作執行被另一個篩選條件中斷,則為 true。
-
Exception - 如果動作或先前所執行的動作篩選條件擲回例外狀況,則為非 Null。 將此屬性設定為 Null:
- 有效地處理例外狀況。
- 執行
Result
,彷彿它是從動作方法傳回的。
對於 IAsyncActionFilter
,呼叫 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);
}
}
}
注意
以 [ApiController]
屬性標注的控制器會自動驗證模型狀態,並傳回 400 回應。 如需詳細資訊,請參閱自動 HTTP 400 回應。
OnActionExecuted
方法會在動作方法之後執行:
- 並可以透過 Result 屬性查看及操作動作的結果。
- Canceled 會在動作執行被另一個篩選器中斷時被設為 true。
-
Exception 當動作或後續的動作過濾器擲回例外狀況時,會被設為非 Null 值。 將
Exception
設定為 null:- 有效處理例外狀況。
- 執行
ActionExecutedContext.Result
時,猶如它是從動作方法正常傳回的。
例外狀況篩選條件
例外狀況篩選條件:
- 實作 IExceptionFilter 或 IAsyncExceptionFilter。
- 可以用來實作常見的錯誤處理原則。
下列範例例外狀況篩選會顯示在開發應用程式時發生的例外狀況詳細資料:
public class SampleExceptionFilter : IExceptionFilter
{
private readonly IHostEnvironment _hostEnvironment;
public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
_hostEnvironment = hostEnvironment;
public void OnException(ExceptionContext context)
{
if (!_hostEnvironment.IsDevelopment())
{
// Don't display exception details unless running in Development.
return;
}
context.Result = new ContentResult
{
Content = context.Exception.ToString()
};
}
}
下列程式碼會測試例外狀況篩選:
[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
例外狀況篩選條件:
- 沒有之前和之後的事件。
- 實作 OnException 或 OnExceptionAsync。
- 處理在 Razor 頁面或控制器建立、模型繫結、操作篩選器或操作方法中發生的未處理例外狀況。
- 不要攔截在資源篩選條件、結果篩選條件或 MVC 結果執行中發生的例外狀況。
若要處理例外狀況,請將 ExceptionHandled 屬性設定為 true
或指派 Result 屬性。 這樣會阻止傳播例外狀況。 例外狀況篩選條件無法將例外狀況變成「成功」。 只有行為過濾器可以這麼做。
例外狀況篩選條件:
- 適合用於捕捉在操作中發生的例外狀況。
- 不像錯誤處理中介軟體那麼有彈性。
偏好使用中介軟體進行例外狀況處理。 只有在錯誤處理會根據所呼叫的動作方法而「不同」時才使用例外狀況篩選條件。 比方說,應用程式可能會有適用於 API 端點及檢視/HTML 的動作方法。 API 端點可能將錯誤資訊傳回為 JSON,而以檢視為基礎的動作則可能將錯誤頁面傳回為 HTML。
結果篩選條件
結果篩選條件:
- 實作介面:
- 他們的執行是圍繞著動作結果的執行進行的。
IResultFilter 和 IAsyncResultFilter
下列程式碼會顯示範例結果篩選條件:
public class SampleResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
// Do something before the result executes.
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Do something after the result executes.
}
}
執行的結果類型會取決於動作。 執行 ViewResult 時傳回檢視的動作包含所有的 Razor 處理。 API 方法可能在結果執行當中執行某種序列化。 深入了解動作結果。
只有在動作或動作篩選產生動作結果時,才會執行結果篩選。 在下列情況下不會執行結果篩選:
- 授權篩選器或資源篩選器將使管線短路。
- 例外篩選器透過產生動作結果來處理例外狀況。
透過將 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 設置為 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel,true
方法可以中斷動作結果以及後續結果篩選的執行。 在短路時寫入回應物件,以避免產生空的回應。 拋出IResultFilter.OnResultExecuting
中的例外狀況:
- 防止執行動作結果和後續的過濾器。
- 視為失敗,而不是成功的結果。
當 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 方法執行時,回應可能已傳送至用戶端。 如果回應已傳送至用戶端,則無法變更。
如果動作結果的執行被另一個篩選器短路,則ResultExecutedContext.Canceled
會被設為 true
。
如果動作結果或後續結果篩選器引發例外狀況,則 ResultExecutedContext.Exception
會被設為非 Null 值。 將 Exception
設為 null 可以有效處理例外狀況,並防止在管線中稍後再次拋出例外狀況。 並沒有可以在處理結果篩選條件中的例外狀況時,將資料寫入回應的可靠方式。 如果當動作結果引發例外時,標頭已經被送出到客戶端,則沒有可靠的機制來傳送錯誤代碼。
對 IAsyncResultFilter 來說,在 await next
上呼叫 ResultExecutionDelegate 將執行任何後續的結果篩選和動作結果。 若要短路,請將 ResultExecutingContext.Cancel 設為 true
,且不要呼叫 ResultExecutionDelegate
。
public class SampleAsyncResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is not EmptyResult)
{
await next();
}
else
{
context.Cancel = true;
}
}
}
架構提供抽象 ResultFilterAttribute
,其可被子類別化。 先前所示的 ResponseHeaderAttribute 類別是結果篩選條件屬性的範例。
IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter
IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter 介面會宣告針對所有動作結果執行的 IResultFilter 實作。 這包括下列動作所產生的動作結果:
- 短路的授權篩選器和資源篩選器。
- 例外狀況篩選。
例如,下列篩選一律會執行,並會在內容交涉失敗時,為動作結果 (ObjectResult) 設定「422 無法處理的實體」狀態碼:
public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult
&& statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
{
context.Result = new ObjectResult("Unprocessable")
{
StatusCode = StatusCodes.Status422UnprocessableEntity
};
}
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
IFilterFactory
IFilterFactory 實現 IFilterMetadata。 因此,可以在篩選條件管線中的任何地方將 IFilterFactory
實例作為 IFilterMetadata
實例使用。 當執行階段準備要叫用篩選條件時,它會嘗試將它轉換成 IFilterFactory
。 如果該轉換成功,系統會呼叫 CreateInstance 方法來建立被叫用的 IFilterMetadata
執行個體。 因為應用程式啟動時不需要明確設定精確的過濾器管線,所以這樣可以提供一個具有彈性的設計。
IFilterFactory.IsReusable
:
- 這是處理站的提示,表示處理站所建立的篩選實例可以在其建立的要求範圍之外重複使用。
- 不應該搭配仰賴具有非單例生命週期之服務的篩選器使用。
ASP.NET Core 執行階段並不保證:
- 將會建立篩選器的單一執行個體。
- 將不會於稍後的時間從 DI 容器重新要求篩選條件。
警告
只有在篩選來源明確、篩選是無狀態,而且篩選可安全地用於多個 HTTP 要求時,才能將 IFilterFactory.IsReusable 設定為傳回 true
。 例如,如果 IFilterFactory.IsReusable
傳回 true
,則不要從註冊為限定範圍或暫時性的 DI 傳回篩選。
可以使用自訂屬性實作作為另一種建立篩選條件的方法,來實作 IFilterFactory
:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
篩選條件會在下列程式碼中套用:
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
IFilterFactory 在屬性上實作
實作 IFilterFactory
的篩選條件很適合用於下列類型的篩選條件:
- 不需要傳遞參數。
- 有需要由 DI 滿足的建構函式相依性。
TypeFilterAttribute 實現 IFilterFactory。
IFilterFactory
會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。
CreateInstance
會從服務容器 (DI) 載入指定的類型。
public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
public SampleActionTypeFilterAttribute()
: base(typeof(InternalSampleActionFilter)) { }
private class InternalSampleActionFilter : IActionFilter
{
private readonly ILogger<InternalSampleActionFilter> _logger;
public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
_logger = logger;
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
}
}
}
下列程式碼會顯示套用篩選條件的三種方式:
[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
[TypeFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithTypeFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
[ServiceFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithServiceFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");
在上述程式碼中,偏好使用套用篩選的第一種方法。
在篩選管道中使用中介軟體
資源篩選條件的運作與中介軟體相似之處在於,它們會圍繞管線中稍後的所有項目執行。 但篩選條件與中介軟體不同之處在於,它們是執行階段的一部分,這表示它們能存取內容和建構。
若要使用中介軟體作為篩選條件,請建立一個具有 Configure
方法的類型,其能指定要插入到篩選條件管線的中介軟體。 下列範例會使用中介軟體來設定回應標頭:
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
使用 MiddlewareFilterAttribute 來執行中介軟體:
[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
中介軟體篩選會在管線的相同階段運行,與資源篩選在模型繫結之前以及管線的其餘部分之後運行。
執行緒安全
將篩選器的
其他資源
- 檢視或下載範例 \(英文\) (如何下載)。
- ASP.NET Core 中 Razor Pages 的篩選方法
作者:Kirk Larkin \(英文\)、Rick Anderson \(英文\)、Tom Dykstra \(英文\) 及 Steve Smith \(英文\)
ASP.NET Core 中的篩選條件可讓程式碼在要求處理管線中的特定階段之前或之後執行。
內建篩選器處理的工作包括:
- 授權,避免存取使用者未獲授權的資源。
- 回應快取,縮短要求管線,傳回快取的回應。
可以建立自訂篩選條件來處理跨領域關注。 跨領域關注的範例包括錯誤處理、快取、設定、授權及記錄。 篩選能避免重複的程式碼。 例如,錯誤處理的例外篩選器可以統一處理錯誤。
本文件適用於 Razor Pages、API 控制器,以及具有檢視的控制器。 篩選無法直接與 Razor 元件搭配使用。 篩選條件只能在以下情況下間接影響元件:
- 元件內嵌在頁面或檢視中。
- 頁面或控制器和檢視會使用篩選。
篩選條件如何運作
篩選條件會在「ASP.NET Core 動作引動過程管線」中執行,其有時也被稱為「篩選條件管線」。 ASP.NET Core 選取完要執行的動作後,會執行篩選條件管線:
篩選類型
每種過濾器類型會在過濾器管道的不同階段執行:
-
- 先跑。
- 判斷使用者是否獲得此要求的授權。
- 如果請求未獲授權,則中斷管道。
-
- 會在授權之後執行。
-
OnResourceExecuting 在篩選管線的其餘部分之前執行程式碼。 例如,
OnResourceExecuting
會在模型繫結之前執行程式碼。 - OnResourceExecuted 在整個管線完成後執行程式碼。
-
- 執行於呼叫動作方法之前和之後。
- 可以變更傳遞至動作的引數。
- 可以變更從動作傳回的結果。
- 不支援在Razor Pages 中。
端點篩選:
- 執行於呼叫動作方法之前和之後。
- 可以變更傳遞至動作的引數。
- 可以變更從動作傳回的結果。
- 不支援在Razor Pages 中。
- 可以在動作和以路由處理常式為基礎的端點上叫用。
例外狀況篩選條件會將通用原則套用到在寫入回應主體之前發生的未處理例外狀況。
-
- 在執行動作結果之前和之後立即執行。
- 只有當動作方法成功執行時,才會執行。
- 適用於必須包圍檢視或格式器執行的邏輯。
下圖顯示篩選條件類型如何在篩選條件管線中互動:
Razor 頁面也支援頁面篩選,此篩選會在頁面處理常式之前和之後執行。
實施
篩選條件同時支援透過不同介面定義的同步和非同步實作。
同步篩選可以在其管線階段之前和之後執行。 例如,系統會在呼叫動作方法之前呼叫 OnActionExecuting。 系統會在傳回動作方法之後呼叫 OnActionExecuted:
public class SampleActionFilter : 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
方法。 例如 OnActionExecutionAsync:
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
// Do something before the action executes.
await next();
// Do something after the action executes.
}
}
在上述程式碼中,SampleAsyncActionFilter
具有會執行動作方法的 ActionExecutionDelegate (next
)。
多個篩選條件階段
可以在單一類別中實作多個篩選條件階段的介面。 例如,ActionFilterAttribute 類別會實作:
請實作同步或非同步版本的篩選介面,但避免同時實作兩者。 執行階段會先檢查以查看篩選條件是否會實作非同步介面,如果是,便會呼叫該介面。 如果沒有,它會呼叫同步介面的一個或多個方法。 如果同時在單一類別中實作非同步和同步介面,系統只會呼叫非同步方法。 使用像 ActionFilterAttribute 這樣的抽象類別時,請僅覆寫每個篩選器類型的同步方法或非同步方法。
內建篩選條件屬性
ASP.NET Core 包含內建的屬性型篩選條件,可對其進行子類別化和自訂。 例如,下列結果篩選條件會將標頭新增至回應:
public class ResponseHeaderAttribute : ActionFilterAttribute
{
private readonly string _name;
private readonly string _value;
public ResponseHeaderAttribute(string name, string value) =>
(_name, _value) = (name, value);
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(_name, _value);
base.OnResultExecuting(context);
}
}
屬性可讓篩選條件接受引數,如上述範例所示。 將 ResponseHeaderAttribute
新增至控制器或動作方法,並指定 HTTP 標頭的名稱和值:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
使用瀏覽器開發人員工具之類的工具來檢查標頭。
回應標頭底下會顯示 filter-header: Filter Value
。
下列程式碼會同時將 ResponseHeaderAttribute
套用到控制器和動作:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
[ResponseHeader("Another-Filter-Header", "Another Filter Value")]
public IActionResult Multiple() =>
Content("Examine the response headers using the F12 developer tools.");
}
Multiple
動作的回應會包括下列標頭:
filter-header: Filter Value
another-filter-header: Another Filter Value
有幾個篩選條件介面有對應的屬性,可用來作為自訂實作的基底類別。
篩選條件屬性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
篩選條件無法套用至 Razor 頁面處理方法。 可以套用至 Razor Page 模型或套用至全域。
篩選條件範圍和執行的順序
篩選條件可以在三種「範圍」之一被添加到管線中。
- 針對控制器或 Razor 頁面使用屬性。
- 在控制器的動作上使用屬性。 篩選屬性無法套用至 Razor Pages 處理常式方法。
- 可全域套用至所有控制器、動作和頁面,如下列程式碼所示:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
預設執行順序
當管線的特定階段有多個篩選條件時,範圍會決定篩選條件的預設執行順序。 全域篩選條件會圍繞類別篩選條件,後者又圍繞方法篩選條件。
因為篩選條件巢狀結構的原因,篩選條件的「之後」程式碼的執行順序會與「之前」程式碼相反。 篩選條件序列:
- 全域篩選條件之前的程式碼。
- 控制器篩選條件的「之前」程式碼。
- 動作方法篩選條件的「之前」程式碼。
- 在動作方法篩選器之後的程式碼。
- 控制器篩選條件的「之後」程式碼。
- 控制器篩選條件的「之前」程式碼。
- 全域篩選條件的後置程式碼。
下列範例說明針對同步動作篩選執行篩選方法的順序:
序列 | 篩選條件範圍 | 過濾方法 |
---|---|---|
1 | 全球 | OnActionExecuting |
2 | 控制器 | OnActionExecuting |
3 | 動作 | OnActionExecuting |
4 | 動作 | OnActionExecuted |
5 | 控制器 | OnActionExecuted |
6 | 全球 | OnActionExecuted |
控制器層級篩選
繼承自 Controller 的每個控制器都包含 OnActionExecuting、OnActionExecutionAsync 和 OnActionExecuted 方法。 這些方法會包裝針對指定動作執行的篩選條件:
-
OnActionExecuting
會在任何動作的篩選之前執行。 -
OnActionExecuted
會在動作的所有篩選之後執行。 -
OnActionExecutionAsync
會在任何動作的篩選之前執行。 呼叫next
之後的程式碼會在動作的篩選之後執行。
下列 ControllerFiltersController
類別:
- 將
SampleActionFilterAttribute
([SampleActionFilter]
) 套用至控制器。 - 覆寫
OnActionExecuting
和OnActionExecuted
。
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");
base.OnActionExecuted(context);
}
public IActionResult Index()
{
Console.WriteLine(
$"- {nameof(ControllerFiltersController)}.{nameof(Index)}");
return Content("Check the Console.");
}
}
瀏覽至 https://localhost:<port>/ControllerFilters
執行下列程式碼:
ControllerFiltersController.OnActionExecuting
GlobalSampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
ControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
控制器層級篩選會將 Order 屬性設定為 int.MinValue
。 控制器層級篩選無法設定為在套用至方法的篩選之後執行。 下一節會說明順序。
針對 Razor 頁面,請參閱覆寫篩選方法以實作 Razor 頁面篩選。
覆寫預設順序
可以藉由實作 IOrderedFilter 來覆寫預設執行序列。
IOrderedFilter
會公開 Order 屬性,其優先順序會高於範圍以決定執行順序。 具有較低 Order
值的篩選條件:
- 在具有較高 值的篩選條件之前執行「之前」
Order
程式碼。 - 在具有較高 值的篩選條件之後執行「之後」
Order
程式碼。
在控制器層級篩選範例中,GlobalSampleActionFilter
具有全域範圍,因此會在具有控制器範圍的 SampleActionFilterAttribute
之前執行。 若要先執行 SampleActionFilterAttribute
,請將其順序設定為 int.MinValue
:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
若要先執行全域篩選 GlobalSampleActionFilter
,請將其 Order
設定為 int.MinValue
:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
取消和短路
可以藉由設定提供給過濾器方法之 Result 參數上的 ResourceExecutingContext 屬性來短路過濾器管線。 例如,下列資源過濾器會阻止管線的其餘部分執行:
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
在下列程式碼中,[ShortCircuitingResourceFilter]
和 [ResponseHeader]
篩選條件都以 Index
動作方法為目標。
ShortCircuitingResourceFilterAttribute
篩選條件:
- 最先執行,因為它是資源篩選條件,且
ResponseHeaderAttribute
是動作篩選條件。 - 中斷管線的其餘部分。
因此,ResponseHeaderAttribute
篩選條件永遠不會執行對於Index
動作。 假設 ShortCircuitingResourceFilterAttribute
先執行的話,如果這兩個篩選條件都套用在動作方法層級,此行為也會相同。
ShortCircuitingResourceFilterAttribute
會因為其篩選類型而先執行:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
依賴注入
可以依類型或實例新增篩選條件。 如果新增執行個體,該執行個體將用於每個要求。 如果新增了一種類型,它就會被啟用。 由類型啟動的篩選條件表示:
- 系統會針對每個請求建立執行個體。
- 任何建構函式的依賴項目都會由依賴注入 (DI) 所填入。
實作為屬性並直接新增至控制器類別或動作方法的篩選器,無法透過依賴注入 (DI) 提供建構函數的相依性。 建構函式相依性無法由 DI 提供,因為屬性必須在其套用位置提供其建構函式參數。
下列篩選條件支援由 DI 所提供的建構函式相依性:
上述篩選條件可以被套用至控制器或動作。
記錄器可從 DI 獲得。 不過,請避免僅針對記錄目的建立並使用篩選條件。 內建框架日誌通常便足以滿足記錄需求。 記錄已新增至篩選條件:
- 應該重點關注篩選器相關的商業領域問題或具體行為。
- 不應該記錄動作或其他架構事件。 內建過濾器已記錄動作和架構事件。
ServiceFilterAttribute
服務篩選條件實作類型會註冊在 Program.cs
中。
ServiceFilterAttribute 從 DI 獲取篩選器的實例。
下列程式碼會顯示使用 DI 的 LoggingResponseHeaderFilterService
類別:
public class LoggingResponseHeaderFilterService : IResultFilter
{
private readonly ILogger _logger;
public LoggingResponseHeaderFilterService(
ILogger<LoggingResponseHeaderFilterService> logger) =>
_logger = logger;
public void OnResultExecuting(ResultExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
context.HttpContext.Response.Headers.Add(
nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
}
public void OnResultExecuted(ResultExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
}
}
在下列程式碼中,會將 LoggingResponseHeaderFilterService
新增至 DI 容器:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
在下列程式碼中,ServiceFilter
屬性會從 DI 擷取 LoggingResponseHeaderFilterService
篩選條件的執行個體:
[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
使用 ServiceFilterAttribute
時,設定 ServiceFilterAttribute.IsReusable:
- 可暗示篩選器實例可能會在其創建的請求範圍之外被重複使用。 ASP.NET Core 執行階段並不保證:
- 將會建立篩選器的單一執行個體。
- 將不會於稍後的時間從 DI 容器重新要求篩選條件。
- 不應該與依賴非單例存活期服務的篩選器搭配使用。
ServiceFilterAttribute 實現 IFilterFactory。
IFilterFactory
會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。
CreateInstance
會從 DI 載入指定的類型。
類型篩選屬性
TypeFilterAttribute 類似於 ServiceFilterAttribute,但其類型不會直接從 DI 容器解析。 它會使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 來具現化類型。
由於 TypeFilterAttribute
類型不會直接從 DI 容器解析:
- 使用
TypeFilterAttribute
參考的類型不需要向 DI 容器註冊。 不過它們的相依性會由 DI 容器滿足。 -
TypeFilterAttribute
可以選擇性地接受類型的建構函式引數。
使用 TypeFilterAttribute
時,設定 TypeFilterAttribute.IsReusable:
提示篩選條件實例「可能」在其建立的請求範圍之外重複使用。 ASP.NET Core 執行階段不保證會建立篩選器的單一實例。
不應該搭配依賴非單例存留期服務的篩選器使用。
下列範例示範如何使用 TypeFilterAttribute
將引數傳遞至類型:
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
授權篩選條件
授權篩選條件:
- 是篩選器管道中最先執行的篩選器。
- 控制對操作方法的存取。
- 有之前的方法,但沒有之後的方法。
自訂授權篩選條件需要自訂的授權架構。 最好是設定授權原則或撰寫自訂授權原則,而不要撰寫自訂篩選條件。 內建的授權篩選條件:
- 呼叫授權系統。
- 不會授權要求。
不會在授權篩選條件內擲回例外狀況:
- 該例外狀況將不會被處理。
- 例外篩選條件將不會處理例外。
請考慮在例外狀況於授權篩選條件中發生時發出挑戰。
深入了解授權。
資源篩選條件
資源篩選條件:
- 實作 IResourceFilter 或 IAsyncResourceFilter 介面。
- 執行涵蓋了大部分的篩選流程。
- 只有授權篩選條件會在資源篩選條件之前執行。
資源篩選條件很適合用來縮短大部分的管線。 例如,當快取命中時,快取篩選器可以跳過剩餘的管線處理。
資源篩選條件範例:
先前所示的短路資源篩選器。
DisableFormValueModelBindingAttribute:
- 防止模型繫結存取表單資料。
- 用於大型檔案上傳,以避免將表單資料讀入記憶體。
動作篩選條件
動作篩選條件不會套用到 Razor Pages。 Razor Pages 支援 IPageFilter 與 IAsyncPageFilter。 如需詳細資訊,請參閱 Razor Pages 的篩選方法。
動作篩選條件:
- 實作 IActionFilter 或 IAsyncActionFilter 介面。
- 他們的執行涉及行動方法的執行。
下列程式碼會顯示範例動作篩選條件:
public class SampleActionFilter : 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
會中斷 Action 方法及後續動作篩選器的執行。
在動作方法中丟出例外狀況:
- 防止執行後續篩選條件。
- 和設定
Result
不同,會被視為失敗而非成功結果。
ActionExecutedContext 提供 Controller
與 Result
,加上下列屬性:
- Canceled - 如果動作執行被另一個篩選條件中斷,則為 true。
-
Exception - 如果動作或先前所執行的動作篩選條件擲回例外狀況,則為非 Null。 將此屬性設定為 Null:
- 有效地處理例外狀況。
- 執行
Result
,彷彿它是從動作方法傳回的。
對於 IAsyncActionFilter
,呼叫 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);
}
}
}
注意
以 [ApiController]
屬性標注的控制器會自動驗證模型狀態,並傳回 400 回應。 如需詳細資訊,請參閱自動 HTTP 400 回應。
OnActionExecuted
方法會在動作方法之後執行:
- 並可以透過 Result 屬性查看及操作動作的結果。
- Canceled 會在動作執行被另一個篩選器中斷時被設為 true。
-
Exception 當動作或後續的動作過濾器擲回例外狀況時,會被設為非 Null 值。 將
Exception
設定為 null:- 有效處理例外狀況。
- 執行
ActionExecutedContext.Result
時,猶如它是從動作方法正常傳回的。
例外狀況篩選條件
例外狀況篩選條件:
- 實作 IExceptionFilter 或 IAsyncExceptionFilter。
- 可以用來實作常見的錯誤處理原則。
下列範例例外狀況篩選會顯示在開發應用程式時發生的例外狀況詳細資料:
public class SampleExceptionFilter : IExceptionFilter
{
private readonly IHostEnvironment _hostEnvironment;
public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
_hostEnvironment = hostEnvironment;
public void OnException(ExceptionContext context)
{
if (!_hostEnvironment.IsDevelopment())
{
// Don't display exception details unless running in Development.
return;
}
context.Result = new ContentResult
{
Content = context.Exception.ToString()
};
}
}
下列程式碼會測試例外狀況篩選:
[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
例外狀況篩選條件:
- 沒有之前和之後的事件。
- 實作 OnException 或 OnExceptionAsync。
- 處理在 Razor 頁面或控制器建立、模型繫結、操作篩選器或操作方法中發生的未處理例外狀況。
- 不要攔截在資源篩選條件、結果篩選條件或 MVC 結果執行中發生的例外狀況。
若要處理例外狀況,請將 ExceptionHandled 屬性設定為 true
或指派 Result 屬性。 這樣會阻止傳播例外狀況。 例外狀況篩選條件無法將例外狀況變成「成功」。 只有行為過濾器可以這麼做。
例外狀況篩選條件:
- 適合用於捕捉在操作中發生的例外狀況。
- 不像錯誤處理中介軟體那麼有彈性。
偏好使用中介軟體進行例外狀況處理。 只有在錯誤處理會根據所呼叫的動作方法而「不同」時才使用例外狀況篩選條件。 比方說,應用程式可能會有適用於 API 端點及檢視/HTML 的動作方法。 API 端點可能將錯誤資訊傳回為 JSON,而以檢視為基礎的動作則可能將錯誤頁面傳回為 HTML。
結果篩選條件
結果篩選條件:
- 實作介面:
- 他們的執行是圍繞著動作結果的執行進行的。
IResultFilter 和 IAsyncResultFilter
下列程式碼會顯示範例結果篩選條件:
public class SampleResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
// Do something before the result executes.
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Do something after the result executes.
}
}
執行的結果類型會取決於動作。 執行 ViewResult 時傳回檢視的動作包含所有的 Razor 處理。 API 方法可能在結果執行當中執行某種序列化。 深入了解動作結果。
只有在動作或動作篩選產生動作結果時,才會執行結果篩選。 在下列情況下不會執行結果篩選:
- 授權篩選器或資源篩選器將使管線短路。
- 例外篩選器透過產生動作結果來處理例外狀況。
透過將 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 設置為 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel,true
方法可以中斷動作結果以及後續結果篩選的執行。 在短路時寫入回應物件,以避免產生空的回應。 拋出IResultFilter.OnResultExecuting
中的例外狀況:
- 防止執行動作結果和後續的過濾器。
- 視為失敗,而不是成功的結果。
當 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 方法執行時,回應可能已傳送至用戶端。 如果回應已傳送至用戶端,則無法變更。
如果動作結果的執行被另一個篩選器短路,則ResultExecutedContext.Canceled
會被設為 true
。
如果動作結果或後續結果篩選器引發例外狀況,則 ResultExecutedContext.Exception
會被設為非 Null 值。 將 Exception
設為 null 可以有效處理例外狀況,並防止在管線中稍後再次拋出例外狀況。 並沒有可以在處理結果篩選條件中的例外狀況時,將資料寫入回應的可靠方式。 如果當動作結果引發例外時,標頭已經被送出到客戶端,則沒有可靠的機制來傳送錯誤代碼。
對 IAsyncResultFilter 來說,在 await next
上呼叫 ResultExecutionDelegate 將執行任何後續的結果篩選和動作結果。 若要短路,請將 ResultExecutingContext.Cancel 設為 true
,且不要呼叫 ResultExecutionDelegate
。
public class SampleAsyncResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is not EmptyResult)
{
await next();
}
else
{
context.Cancel = true;
}
}
}
架構提供抽象 ResultFilterAttribute
,其可被子類別化。 先前所示的 ResponseHeaderAttribute 類別是結果篩選條件屬性的範例。
IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter
IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter 介面會宣告針對所有動作結果執行的 IResultFilter 實作。 這包括下列動作所產生的動作結果:
- 短路的授權篩選器和資源篩選器。
- 例外狀況篩選。
例如,下列篩選一律會執行,並會在內容交涉失敗時,為動作結果 (ObjectResult) 設定「422 無法處理的實體」狀態碼:
public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult
&& statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
{
context.Result = new ObjectResult("Unprocessable")
{
StatusCode = StatusCodes.Status422UnprocessableEntity
};
}
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
IFilterFactory
IFilterFactory 實現 IFilterMetadata。 因此,可以在篩選條件管線中的任何地方將 IFilterFactory
實例作為 IFilterMetadata
實例使用。 當執行階段準備要叫用篩選條件時,它會嘗試將它轉換成 IFilterFactory
。 如果該轉換成功,系統會呼叫 CreateInstance 方法來建立被叫用的 IFilterMetadata
執行個體。 因為應用程式啟動時不需要明確設定精確的過濾器管線,所以這樣可以提供一個具有彈性的設計。
IFilterFactory.IsReusable
:
- 這是處理站的提示,表示處理站所建立的篩選實例可以在其建立的要求範圍之外重複使用。
- 不應該搭配仰賴具有非單例生命週期之服務的篩選器使用。
ASP.NET Core 執行階段並不保證:
- 將會建立篩選器的單一執行個體。
- 將不會於稍後的時間從 DI 容器重新要求篩選條件。
警告
只有在篩選來源明確、篩選是無狀態,而且篩選可安全地用於多個 HTTP 要求時,才能將 IFilterFactory.IsReusable 設定為傳回 true
。 例如,如果 IFilterFactory.IsReusable
傳回 true
,則不要從註冊為限定範圍或暫時性的 DI 傳回篩選。
可以使用自訂屬性實作作為另一種建立篩選條件的方法,來實作 IFilterFactory
:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
篩選條件會在下列程式碼中套用:
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
IFilterFactory 在屬性上實作
實作 IFilterFactory
的篩選條件很適合用於下列類型的篩選條件:
- 不需要傳遞參數。
- 有需要由 DI 滿足的建構函式相依性。
TypeFilterAttribute 實現 IFilterFactory。
IFilterFactory
會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。
CreateInstance
會從服務容器 (DI) 載入指定的類型。
public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
public SampleActionTypeFilterAttribute()
: base(typeof(InternalSampleActionFilter)) { }
private class InternalSampleActionFilter : IActionFilter
{
private readonly ILogger<InternalSampleActionFilter> _logger;
public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
_logger = logger;
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
}
}
}
下列程式碼會顯示套用篩選條件的三種方式:
[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");
在上述程式碼中,偏好使用套用篩選的第一種方法。
在篩選管道中使用中介軟體
資源篩選條件的運作與中介軟體相似之處在於,它們會圍繞管線中稍後的所有項目執行。 但篩選條件與中介軟體不同之處在於,它們是執行階段的一部分,這表示它們能存取內容和建構。
若要使用中介軟體作為篩選條件,請建立一個具有 Configure
方法的類型,其能指定要插入到篩選條件管線的中介軟體。 下列範例會使用中介軟體來設定回應標頭:
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
使用 MiddlewareFilterAttribute 來執行中介軟體:
[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
中介軟體篩選會在管線的相同階段運行,與資源篩選在模型繫結之前以及管線的其餘部分之後運行。
執行緒安全
將篩選器的
其他資源
- 檢視或下載範例 \(英文\) (如何下載)。
- ASP.NET Core 中 Razor Pages 的篩選方法
作者:Kirk Larkin \(英文\)、Rick Anderson \(英文\)、Tom Dykstra \(英文\) 及 Steve Smith \(英文\)
ASP.NET Core 中的「篩選條件」可讓程式碼在要求處理管線中的特定階段之前或之後執行。
內建篩選器處理的工作包括:
- 授權,避免存取使用者未獲授權的資源。
- 回應快取,縮短要求管線,傳回快取的回應。
可以建立自訂篩選條件來處理跨領域關注。 跨領域關注的範例包括錯誤處理、快取、設定、授權及記錄。 篩選能避免重複的程式碼。 例如,錯誤處理的例外篩選器可以統一處理錯誤。
本文件適用於 Razor Pages、API 控制器,以及具有檢視的控制器。 篩選無法直接與 Razor 元件搭配使用。 篩選條件只能在以下情況下間接影響元件:
- 元件內嵌在頁面或檢視中。
- 頁面或控制器和檢視會使用篩選。
篩選條件如何運作
篩選條件會在「ASP.NET Core 動作引動過程管線」中執行,其有時也被稱為「篩選條件管線」。 篩選條件管線是在 ASP.NET Core 選取要執行的動作之後運行的。
篩選類型
每種過濾器類型會在過濾器管道的不同階段執行:
授權篩選條件會先執行,用來判斷使用者是否已針對要求取得授權。 如果請求未被授權,授權篩選器會中斷管道。
-
- 會在授權之後執行。
-
OnResourceExecuting 在篩選管線的其餘部分之前執行程式碼。 例如,
OnResourceExecuting
會在模型繫結之前執行程式碼。 - OnResourceExecuted 在整個管線完成後執行程式碼。
-
- 在呼叫動作方法之前和之後立即執行程式碼。
- 可以變更傳遞至動作的引數。
- 可以變更從動作傳回的結果。
- 不支援在Razor Pages 中。
例外狀況篩選條件會將通用原則套用到在寫入回應主體之前發生的未處理例外狀況。
結果篩選條件在執行動作結果前後運行程式碼。 它們只有在動作方法執行成功時才執行。 它們非常適合用於需要環繞視圖或格式化器執行的邏輯。
下圖顯示篩選條件類型如何在篩選條件管線中互動。
實施
篩選條件同時支援透過不同介面定義的同步和非同步實作。
同步過濾器會在其管線階段之前和之後執行程式碼。 例如,系統會在呼叫動作方法之前呼叫 OnActionExecuting。 系統會在傳回動作方法之後呼叫 OnActionExecuted。
public class MySampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
}
在上述程式碼中,MyDebug 是下載範例中的公用程式函式。
非同步篩選會定義 On-Stage-ExecutionAsync
方法。 例如 OnActionExecutionAsync:
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
)。
多個篩選條件階段
可以在單一類別中實作多個篩選條件階段的介面。 例如,ActionFilterAttribute 類別會實作:
請實作同步或非同步版本的篩選介面,但避免同時實作兩者。 執行階段會先檢查以查看篩選條件是否會實作非同步介面,如果是,便會呼叫該介面。 如果沒有,它會呼叫同步介面的一個或多個方法。 如果同時在單一類別中實作非同步和同步介面,系統只會呼叫非同步方法。 使用像 ActionFilterAttribute 這樣的抽象類別時,請僅覆寫每個篩選器類型的同步方法或非同步方法。
內建篩選條件屬性
ASP.NET Core 包含內建的屬性型篩選條件,可對其進行子類別化和自訂。 例如,下列結果篩選條件會將標頭新增至回應:
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", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
使用瀏覽器開發人員工具之類的工具來檢查標頭。
回應標頭底下會顯示 author: Rick Anderson
。
下列程式碼會實作 ActionFilterAttribute
來執行下列動作:
- 從組態系統讀取標題和名稱。 不同於先前的範例,下列程式碼不需要將篩選參數新增至程式碼。
- 將標題和名稱新增至回應標頭。
public class MyActionFilterAttribute : ActionFilterAttribute
{
private readonly PositionOptions _settings;
public MyActionFilterAttribute(IOptions<PositionOptions> options)
{
_settings = options.Value;
Order = 1;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(_settings.Title,
new string[] { _settings.Name });
base.OnResultExecuting(context);
}
}
組態選項會透過選項模式從組態系統中提供。 例如,來自 appsettings.json
檔案:
{
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
在 StartUp.ConfigureServices
中:
-
PositionOptions
類別會透過"Position"
組態區域來新增到服務容器。 -
MyActionFilterAttribute
已新增至服務容器。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(
Configuration.GetSection("Position"));
services.AddScoped<MyActionFilterAttribute>();
services.AddControllersWithViews();
}
下列程式碼顯示 PositionOptions
類別:
public class PositionOptions
{
public string Title { get; set; }
public string Name { get; set; }
}
下列程式碼會將 MyActionFilterAttribute
套用至 Index2
方法:
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ServiceFilter(typeof(MyActionFilterAttribute))]
public IActionResult Index2()
{
return Content("Header values by configuration.");
}
在回應標頭底下,author: Rick Anderson
和Editor: Joe Smith
會在呼叫Sample/Index2
端點時顯示。
下列程式碼會將 MyActionFilterAttribute
和 AddHeaderAttribute
套用至 Razor Page:
[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
篩選條件無法套用至 Razor 頁面處理方法。 可以套用至 Razor Page 模型或套用至全域。
有幾個篩選條件介面有對應的屬性,可用來作為自訂實作的基底類別。
篩選條件屬性:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
篩選條件範圍和執行的順序
篩選條件可以在三種「範圍」之一被添加到管線中。
- 在控制器的動作上使用屬性。 篩選屬性無法套用至 Razor Pages 處理常式方法。
- 針對控制器或 Razor 頁面使用屬性。
- 可全域套用至所有控制器、動作和頁面,如下列程式碼所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
預設執行順序
當管線的特定階段有多個篩選條件時,範圍會決定篩選條件的預設執行順序。 全域篩選條件會圍繞類別篩選條件,後者又圍繞方法篩選條件。
因為篩選條件巢狀結構的原因,篩選條件的「之後」程式碼的執行順序會與「之前」程式碼相反。 篩選條件序列:
- 全域篩選條件之前的程式碼。
-
之前控制器和Razor Page 篩選條件的程式碼。
- 動作方法篩選條件的「之前」程式碼。
- 在動作方法篩選器之後的程式碼。
- 控制器程式碼和頁面篩選器的之後程式碼。
-
之前控制器和Razor Page 篩選條件的程式碼。
- 全域篩選條件的後置程式碼。
下列範例說明針對同步操作篩選器呼叫篩選器方法的順序。
序列 | 篩選條件範圍 | 過濾方法 |
---|---|---|
1 | 全球 | OnActionExecuting |
2 | 控制器或Razor頁面 | OnActionExecuting |
3 | 方法 | OnActionExecuting |
4 | 方法 | OnActionExecuted |
5 | 控制器或Razor頁面 | OnActionExecuted |
6 | 全球 | OnActionExecuted |
控制器層級篩選
繼承自 Controller 基底類別的每個控制器,都包含 Controller.OnActionExecuting、Controller.OnActionExecutionAsync 和 Controller.OnActionExecutedOnActionExecuted
方法。 這些方法:
- 封裝在特定動作中運行的過濾器。
- 系統會在呼叫任何動作的篩選器之前呼叫
OnActionExecuting
。 - 在所有動作篩選器之後會呼叫
OnActionExecuted
。 - 系統會在呼叫任何動作的篩選器之前呼叫
OnActionExecutionAsync
。 在動作方法之後執行next
後,位於篩選條件中的程式碼。
例如,在下載範例中,MySampleActionFilter
會在啟動時全域套用。
TestController
:
- 將
SampleActionFilterAttribute
([SampleActionFilter]
) 套用至FilterTest2
動作。 - 覆寫
OnActionExecuting
和OnActionExecuted
。
public class TestController : Controller
{
[SampleActionFilter(Order = int.MinValue)]
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
MyDisplayRouteInfo 是由 Rick.Docs.Samples.RouteInfo NuGet 套件提供,並顯示路由資訊。
瀏覽至 https://localhost:5001/Test/FilterTest2
執行下列程式碼:
TestController.OnActionExecuting
MySampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
TestController.FilterTest2
SampleActionFilterAttribute.OnActionExecuted
MySampleActionFilter.OnActionExecuted
TestController.OnActionExecuted
控制器層級篩選會將 Order 屬性設定為 int.MinValue
。 控制器層級篩選無法設定為在套用至方法的篩選之後執行。 下一節會說明順序。
針對 Razor 頁面,請參閱覆寫篩選方法以實作 Razor 頁面篩選。
覆寫預設順序
可以藉由實作 IOrderedFilter 來覆寫預設執行序列。
IOrderedFilter
會公開 Order 屬性,其優先順序會高於範圍以決定執行順序。 具有較低 Order
值的篩選條件:
- 在具有較高 值的篩選條件之前執行「之前」
Order
程式碼。 - 在具有較高 值的篩選條件之後執行「之後」
Order
程式碼。
Order
屬性可以搭配建構函式參數設定:
[SampleActionFilter(Order = int.MinValue)]
請考慮下列控制器中的兩個動作篩選:
[MyAction2Filter]
public class Test2Controller : Controller
{
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
StartUp.ConfigureServices
中會新增全域篩選:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
3 個篩選條件會依下列循序執行:
Test2Controller.OnActionExecuting
MySampleActionFilter.OnActionExecuting
MyAction2FilterAttribute.OnActionExecuting
Test2Controller.FilterTest2
MyAction2FilterAttribute.OnResultExecuting
MySampleActionFilter.OnActionExecuted
Test2Controller.OnActionExecuted
Order
屬性在決定篩選器執行的順序時會覆寫範圍。 篩選條件會先依照順序排序,然後使用範圍來打破僵局。 所有內建的篩選條件都會實作 IOrderedFilter
並將預設 Order
值設為 0。 如先前所述,控制器層級篩選會將 Order 屬性設定為 int.MinValue
針對內建篩選,範圍會決定順序,除非 Order
設定為非零值。
在上述程式碼中,MySampleActionFilter
具有全域範圍,因此會在具有控制器範圍的 MyAction2FilterAttribute
之前執行。 若要先執行 MyAction2FilterAttribute
,請將順序設定為 int.MinValue
:
[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
若要先執行全域篩選 MySampleActionFilter
,請將 Order
設定為 int.MinValue
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter),
int.MinValue);
});
}
取消和短路
可以藉由設定提供給過濾器方法之 Result 參數上的 ResourceExecutingContext 屬性來短路過濾器管線。 例如,下列資源篩選器會防止管線的其餘部分執行:
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
篩選條件都以 SomeResource
動作方法為目標。
ShortCircuitingResourceFilter
:
- 最先執行,因為它是資源篩選條件,且
AddHeader
是動作篩選條件。 - 中斷管線的其餘部分。
因此,AddHeader
篩選條件永遠不會執行對於SomeResource
動作。 假設 ShortCircuitingResourceFilter
先執行的話,如果這兩個篩選條件都套用在動作方法層級,此行為也會相同。
ShortCircuitingResourceFilter
會因為其篩選器類型而先執行,或藉由明確使用 Order
屬性而先執行。
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ServiceFilter(typeof(MyActionFilterAttribute))]
public IActionResult Index2()
{
return Content("Header values by configuration.");
}
[ShortCircuitingResourceFilter]
public IActionResult SomeResource()
{
return Content("Successful access to resource - header is set.");
}
[AddHeaderWithFactory]
public IActionResult HeaderWithFactory()
{
return Content("Examine the headers using the F12 developer tools.");
}
}
依賴注入
可以依類型或實例新增篩選條件。 如果新增執行個體,該執行個體將用於每個要求。 如果新增了一種類型,它就會被啟用。 由類型啟動的篩選條件表示:
- 系統會針對每個請求建立執行個體。
- 任何建構函式的依賴項目都會由依賴注入 (DI) 所填入。
實作為屬性並直接新增至控制器類別或動作方法的篩選器,無法透過依賴注入 (DI) 提供建構函數的相依性。 DI 無法提供建構函式相依性,因為:
- 屬性的建構函式參數必須在應用的地方提供。
- 這是屬性運作方式的限制。
下列篩選條件支援由 DI 所提供的建構函式相依性:
上述篩選條件可以被套用至控制器或動作方法:
記錄器可從 DI 獲得。 不過,請避免僅針對記錄目的建立並使用篩選條件。 內建框架日誌通常便足以滿足記錄需求。 記錄已新增至篩選條件:
- 應該重點關注篩選器相關的商業領域問題或具體行為。
- 不應該記錄動作或其他架構事件。 內建篩選功能能記錄動作和框架事件。
ServiceFilterAttribute
服務篩選條件實作類型會註冊在 ConfigureServices
中。
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.
_logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
}
}
在下列程式碼中,會將 AddHeaderResultServiceFilter
新增至 DI 容器:
public void ConfigureServices(IServiceCollection services)
{
// Add service filters.
services.AddScoped<AddHeaderResultServiceFilter>();
services.AddScoped<SampleActionFilterAttribute>();
services.AddControllersWithViews(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
});
}
在下列程式碼中,ServiceFilter
屬性會從 DI 擷取 AddHeaderResultServiceFilter
篩選條件的執行個體:
[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
return View();
}
使用 ServiceFilterAttribute
時,設定 ServiceFilterAttribute.IsReusable:
可暗示篩選器實例可能會在其創建的請求範圍之外被重複使用。 ASP.NET Core 執行階段並不保證:
- 將會建立篩選器的單一執行個體。
- 將不會於稍後的時間從 DI 容器重新要求篩選條件。
不應該搭配依賴非單例存留期服務的篩選器使用。
ServiceFilterAttribute 實現 IFilterFactory。
IFilterFactory
會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。
CreateInstance
會從 DI 載入指定的類型。
類型篩選屬性
TypeFilterAttribute 類似於 ServiceFilterAttribute,但其類型不會直接從 DI 容器解析。 它會使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 來具現化類型。
由於 TypeFilterAttribute
類型不會直接從 DI 容器解析:
- 使用
TypeFilterAttribute
參考的類型不需要向 DI 容器註冊。 不過它們的相依性會由 DI 容器滿足。 -
TypeFilterAttribute
可以選擇性地接受類型的建構函式引數。
使用 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}");
}
授權篩選條件
授權篩選條件:
- 是篩選器管道中最先執行的篩選器。
- 控制對操作方法的存取。
- 有之前的方法,但沒有之後的方法。
自訂授權篩選條件需要自訂的授權架構。 最好是設定授權原則或撰寫自訂授權原則,而不要撰寫自訂篩選條件。 內建的授權篩選條件:
- 呼叫授權系統。
- 不會授權要求。
不會在授權篩選條件內擲回例外狀況:
- 該例外狀況將不會被處理。
- 例外篩選條件將不會處理例外。
請考慮在例外狀況於授權篩選條件中發生時發出挑戰。
深入了解授權。
資源篩選條件
資源篩選條件:
- 實作 IResourceFilter 或 IAsyncResourceFilter 介面。
- 執行涵蓋了大部分的篩選流程。
- 只有授權篩選條件會在資源篩選條件之前執行。
資源篩選條件很適合用來縮短大部分的管線。 例如,當快取命中時,快取篩選器可以跳過剩餘的管線處理。
資源篩選條件範例:
先前所示的短路資源篩選器。
DisableFormValueModelBindingAttribute:
- 防止模型繫結存取表單資料。
- 用於大型檔案上傳,以避免將表單資料讀入記憶體。
動作篩選條件
動作篩選條件不會套用到 Razor Pages。 Razor Pages 支援 IPageFilter 與 IAsyncPageFilter。 如需詳細資訊,請參閱 Razor Pages 的篩選方法。
動作篩選條件:
- 實作 IActionFilter 或 IAsyncActionFilter 介面。
- 他們的執行涉及行動方法的執行。
下列程式碼會顯示範例動作篩選條件:
public class MySampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
}
ActionExecutingContext 提供下列屬性:
- ActionArguments - 允許讀取動作方法的輸入。
- Controller - 提供對控制器執行個體的管理。
-
Result - 設定
Result
會中斷 Action 方法及後續動作篩選器的執行。
在動作方法中丟出例外狀況:
- 防止執行後續篩選條件。
- 和設定
Result
不同,會被視為失敗而非成功結果。
ActionExecutedContext 提供 Controller
與 Result
,加上下列屬性:
Canceled - 如果動作執行被另一個篩選條件中斷,則為 true。
Exception - 如果動作或先前所執行的動作篩選條件擲回例外狀況,則為非 Null。 將此屬性設定為 Null:
- 有效地處理例外狀況。
- 執行
Result
,彷彿它是從動作方法傳回的。
對於 IAsyncActionFilter
,呼叫 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);
}
}
注意
以 [ApiController]
屬性標注的控制器會自動驗證模型狀態,並傳回 400 回應。 如需詳細資訊,請參閱自動 HTTP 400 回應。
OnActionExecuted
方法會在動作方法之後執行:
並可以透過 Result 屬性查看及操作動作的結果。
Canceled 會在動作執行被另一個篩選器中斷時被設為 true。
Exception 當動作或後續的動作過濾器擲回例外狀況時,會被設為非 Null 值。 將
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);
}
}
例外狀況篩選條件
例外狀況篩選條件:
- 實作 IExceptionFilter 或 IAsyncExceptionFilter。
- 可以用來實作常見的錯誤處理原則。
下列範例例外狀況篩選條件會使用自訂的錯誤檢視,以顯示在開發應用程式時發生的例外狀況詳細資料:
public class CustomExceptionFilter : IExceptionFilter
{
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly IModelMetadataProvider _modelMetadataProvider;
public CustomExceptionFilter(
IWebHostEnvironment 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;
}
}
下列程式碼會測試例外狀況篩選:
[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
[AddHeader("Failing Controller",
"Won't appear when exception is handled")]
public IActionResult Index()
{
throw new Exception("Testing custom exception filter.");
}
}
例外狀況篩選條件:
- 沒有之前和之後的事件。
- 實作 OnException 或 OnExceptionAsync。
- 處理在 Razor 頁面或控制器建立、模型繫結、操作篩選器或操作方法中發生的未處理例外狀況。
- 不要攔截在資源篩選條件、結果篩選條件或 MVC 結果執行中發生的例外狀況。
若要處理例外狀況,請將 ExceptionHandled 屬性設定為 true
或指派 Result 屬性。 這樣會阻止傳播例外狀況。 例外狀況篩選條件無法將例外狀況變成「成功」。 只有行為過濾器可以這麼做。
例外狀況篩選條件:
- 適合用於捕捉在操作中發生的例外狀況。
- 不像錯誤處理中介軟體那麼有彈性。
偏好使用中介軟體進行例外狀況處理。 只有在錯誤處理會根據所呼叫的動作方法而「不同」時才使用例外狀況篩選條件。 比方說,應用程式可能會有適用於 API 端點及檢視/HTML 的動作方法。 API 端點可能將錯誤資訊傳回為 JSON,而以檢視為基礎的動作則可能將錯誤頁面傳回為 HTML。
結果篩選條件
結果篩選條件:
- 實作介面:
- 他們的執行是圍繞著動作結果的執行進行的。
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.
_logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
}
}
執行的結果類型會取決於動作。 執行 ViewResult 時傳回檢視的動作包含所有的 Razor 處理。 API 方法可能在結果執行當中執行某種序列化。 深入了解動作結果。
只有在動作或動作篩選產生動作結果時,才會執行結果篩選。 在下列情況下不會執行結果篩選:
- 授權篩選器或資源篩選器將使管線短路。
- 例外篩選器透過產生動作結果來處理例外狀況。
透過將 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 設置為 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel,true
方法可以中斷動作結果以及後續結果篩選的執行。 在短路時寫入回應物件,以避免產生空的回應。 拋出IResultFilter.OnResultExecuting
中的例外狀況:
- 防止執行動作結果和後續的過濾器。
- 視為失敗,而不是成功的結果。
當 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 方法執行時,回應可能已傳送至用戶端。 如果回應已傳送至用戶端,則無法變更。
如果動作結果的執行被另一個篩選器短路,則ResultExecutedContext.Canceled
會被設為 true
。
如果動作結果或後續結果篩選器引發例外狀況,則 ResultExecutedContext.Exception
會被設為非 Null 值。 將 Exception
設為 null 可以有效處理例外狀況,並防止在管線中稍後再次拋出例外狀況。 並沒有可以在處理結果篩選條件中的例外狀況時,將資料寫入回應的可靠方式。 如果當動作結果引發例外時,標頭已經被送出到客戶端,則沒有可靠的機制來傳送錯誤代碼。
對 IAsyncResultFilter 來說,在 await next
上呼叫 ResultExecutionDelegate 將執行任何後續的結果篩選和動作結果。 若要短路,請將 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;
}
}
}
架構提供抽象 ResultFilterAttribute
,其可被子類別化。 先前所示的 AddHeaderAttribute 類別是結果篩選條件屬性的範例。
IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter
IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter 介面會宣告針對所有動作結果執行的 IResultFilter 實作。 這包括下列動作所產生的動作結果:
- 短路的授權篩選器和資源篩選器。
- 例外狀況篩選。
例如,下列篩選一律會執行,並會在內容交涉失敗時,為動作結果 (ObjectResult) 設定「422 無法處理的實體」狀態碼:
public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult &&
statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
{
context.Result = new ObjectResult("Can't process this!")
{
StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
};
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
IFilterFactory
IFilterFactory 實現 IFilterMetadata。 因此,可以在篩選條件管線中的任何地方將 IFilterFactory
實例作為 IFilterMetadata
實例使用。 當執行階段準備要叫用篩選條件時,它會嘗試將它轉換成 IFilterFactory
。 如果該轉換成功,系統會呼叫 CreateInstance 方法來建立被叫用的 IFilterMetadata
執行個體。 因為應用程式啟動時不需要明確設定精確的過濾器管線,所以這樣可以提供一個具有彈性的設計。
IFilterFactory.IsReusable
:
- 這是處理站的提示,表示處理站所建立的篩選實例可以在其建立的要求範圍之外重複使用。
- 不應該搭配仰賴具有非單例生命週期之服務的篩選器使用。
ASP.NET Core 執行階段並不保證:
- 將會建立篩選器的單一執行個體。
- 將不會於稍後的時間從 DI 容器重新要求篩選條件。
警告
只有在篩選來源明確、篩選是無狀態,而且篩選可安全地用於多個 HTTP 要求時,才能將 IFilterFactory.IsReusable 設定為傳回 true
。 例如,如果 IFilterFactory.IsReusable
傳回 true
,則不要從註冊為限定範圍或暫時性的 DI 傳回篩選。
可以使用自訂屬性實作作為另一種建立篩選條件的方法,來實作 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;
}
}
}
篩選條件會在下列程式碼中套用:
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
[ServiceFilter(typeof(MyActionFilterAttribute))]
public IActionResult Index2()
{
return Content("Header values by configuration.");
}
[ShortCircuitingResourceFilter]
public IActionResult SomeResource()
{
return Content("Successful access to resource - header is set.");
}
[AddHeaderWithFactory]
public IActionResult HeaderWithFactory()
{
return Content("Examine the headers using the F12 developer tools.");
}
}
執行下載範例來測試上述程式碼:
- 叫用 F12 開發人員工具。
- 瀏覽至
https://localhost:5001/Sample/HeaderWithFactory
。
F12 開發人員工具會顯示由範例程式碼所加入的下列回應標頭:
-
作者:
Rick Anderson
-
globaladdheader:
Result filter added to MvcOptions.Filters
-
內部:
My header
上述程式碼會建立 internal:My header
回應標頭。
IFilterFactory 在屬性上實作
實作 IFilterFactory
的篩選條件很適合用於下列類型的篩選條件:
- 不需要傳遞參數。
- 有需要由 DI 滿足的建構函式相依性。
TypeFilterAttribute 實現 IFilterFactory。
IFilterFactory
會公開 CreateInstance 方法來建立 IFilterMetadata 執行個體。
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("SampleActionFilterAttribute.OnActionExecuting");
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
}
}
}
下列程式碼會顯示套用 [SampleActionFilter]
的三種方式:
[SampleActionFilter]
public IActionResult FilterTest()
{
return Content("From FilterTest");
}
[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
return Content("From TypeFilterTest");
}
// 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
的首選方法。
在篩選條件管線中使用中介軟體
資源篩選條件的運作與中介軟體相似之處在於,它們會圍繞管線中稍後的所有項目執行。 但篩選條件與中介軟體不同之處在於,它們是執行階段的一部分,這表示它們能存取內容和建構。
若要使用中介軟體作為篩選條件,請建立一個具有 Configure
方法的類型,其能指定要插入到篩選條件管線的中介軟體。 以下範例會使用當地語系化中介軟體來設置請求的當前文化:
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 範例 \(英文\)。