首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET MVC ControllerActionInvoker.InvokeActionMethodWithFilters 深度剖析

     編輯:關於ASP.NET

深入分析 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();
}

Aggregate 建構委托鏈過程圖解

假設 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

InvokeActionMethodFilter 關鍵邏輯

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

執行順序總結

階段順序說明
OnActionExecutingFilterA → FilterB → ... → FilterN註冊順序,最先註冊最先執行
Action 方法單一執行在所有 OnActionExecuting 通過後
OnActionExecutedFilterN → ... → FilterB → FilterA反向順序,類似堆疊彈出

攔截行為說明

  • 任一 Filter 在 OnActionExecuting 設定 preContext.Result → 短路後續 Filter 與 Action,直接進入反向 OnActionExecuted 階段
  • 類似面試流程:HR 未過 → 不進主管面試 → 直接結束

自訂 Filter 範例:效能記錄

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+)

原始碼學習建議

  1. 下載 ASP.NET MVC 原始碼(CodePlex / GitHub)
  2. 設定符號伺服器,偵錯進入 System.Web.Mvc.dll
  3. 在 InvokeActionMethodWithFilters 設中斷點,觀察委托鏈構建過程
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved