深入分析 ControllerActionInvoker.InvokeActionMethodWithFilters 原始碼,揭示 Action Filter 執行順序、委托鏈構建與 Linq.Aggregate 在管線中的關鍵角色。
protected virtual ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext,
IList<IActionFilter> filters,
ActionDescriptor actionDescriptor,
IDictionary<string, object> parameters) {
// 1. 建立執行前脈絡
ActionExecutingContext preContext = new ActionExecutingContext(
controllerContext, actionDescriptor, parameters);
// 2. 定義核心執行續(continuation):實際呼叫 Action 方法
Func<ActionExecutedContext> continuation = () =>
new ActionExecutedContext(controllerContext, actionDescriptor, false, null) {
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};
// 3. 關鍵:反轉 Filter 列表並用 Aggregate 建構委托鏈
// filters.Reverse() 確保第一個 Filter 最外層(最先執行 OnActionExecuting)
Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
// 4. 執行委托鏈
return thunk();
}
假設 Filters = [FilterA, FilterB](註冊順序),Reverse() 後為 [FilterB, FilterA]
// 初始 continuation = 執行 Action 方法
// 第一輪 Aggregate (FilterB):
// next = continuation
// 回傳 () => InvokeActionMethodFilter(FilterB, preContext, continuation)
// 即:執行 FilterB.OnActionExecuting → 若未攔截 → 執行 continuation (Action)
// 第二輪 Aggregate (FilterA):
// next = 上一輪回傳的委托 (FilterB 鏈)
// 回傳 () => InvokeActionMethodFilter(FilterA, preContext, FilterB鏈)
// 即:執行 FilterA.OnActionExecuting → 若未攔截 → 執行 FilterB 鏈
最終 thunk 為:() => FilterA.OnActionExecuting → FilterB.OnActionExecuting → Action → FilterB.OnActionExecuted → FilterA.OnActionExecuted
private ActionExecutedContext InvokeActionMethodFilter(
IActionFilter filter,
ActionExecutingContext preContext,
Func<ActionExecutedContext> continuation) {
// 1. 執行 OnActionExecuting
filter.OnActionExecuting(preContext);
// 2. 檢查是否被攔截 (Result 已被設定)
if (preContext.Result != null) {
return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true, null) {
Result = preContext.Result
};
}
// 3. 未攔截 → 執行下一環 (continuation)
ActionExecutedContext postContext = continuation();
// 4. 執行 OnActionExecuted
filter.OnActionExecuted(postContext);
return postContext;
}
| 階段 | 順序 | 說明 |
|---|---|---|
| OnActionExecuting | FilterA → FilterB → ... → FilterN | 註冊順序,最先註冊最先執行 |
| Action 方法 | 單一執行 | 在所有 OnActionExecuting 通過後 |
| OnActionExecuted | FilterN → ... → FilterB → FilterA | 反向順序,類似堆疊彈出 |
OnActionExecuting 設定 preContext.Result → 短路後續 Filter 與 Action,直接進入反向 OnActionExecuted 階段public class ProfilingFilter : IActionFilter {
private Stopwatch _sw;
public void OnActionExecuting(ActionExecutingContext filterContext) {
_sw = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
_sw.Stop();
var log = $"{filterContext.ActionDescriptor.ControllerDescriptor.ControllerName}.{filterContext.ActionDescriptor.ActionName} 耗時 {_sw.ElapsedMilliseconds}ms";
System.Diagnostics.Debug.WriteLine(log);
}
}
註冊:GlobalFilters.Filters.Add(new ProfilingFilter());(MVC 3+)
System.Web.Mvc.dllInvokeActionMethodWithFilters 設中斷點,觀察委托鏈構建過程