Web API Controller 是怎樣建成的

ASP.NET Web API 2 的 controller 建立過程圖解...

2014-08-01 更新:我把這篇文章整理到《.NET 相依性注入》的第五章時重新校訂了幾次。更精確,也更詳細了,一併更新到這裡。

欲注入相依物件至 controller 類別的建構函式,或從中攔截某些事件來改變預設行為,必須得先了解 Web API 框架建立 controller 的過程。這篇筆記嘗試用活動圖來說明其中的複雜環節。

首先,HTTP request 在進入 Message Handlers 訊息管線之後,行經數個訊息處理常式,然後來到了管線中的最後一站:IHttpControllerDispatcher 物件。這個 IHttpControllerDispatcher 物件收到 HttpRequestMessage 之後,便開始進行 controller 型別的解析,以便建立目標 controller,然後將控制權交給 controller 物件以進行後續處理。

下圖即為 IHttpControllerDispatcher 物件收到 HttpRequestMessage 之後,一直到 controller 物件建立完成為止的過程。


說明(與圖中數字編號對應):
  1. HttpControllerDispatcher 透過 IHttpControllerSelector 物件的 SelectController 方法來取得目標 controller 型別資訊,這型別資訊是包在一個 HttpControllerDescriptor 物件裡。
  2. HttpControllerDispatcher 接著呼叫 HttpControllerDescriptor 物件的 CreateController 方法,而該方法又會去呼叫 ServicesContainer 物件的 GetHttpControllerActivator 方法來取得 IHttpControllerActivator 物件。以下程式片段摘自 Web API 原始碼,涵蓋了此步驟至下一步驟的部分邏輯:
    // HttpControllerDescriptor 類別的 CreateController 方法。
    public virtual IHttpController CreateController(HttpRequestMessage request)
    {
        // 底下的 Configuration.Services 是個服務容器,後面會說明。
        IHttpControllerActivator activator = 
            Configuration.Services.GetHttpControllerActivator(); 
        IHttpController instance = activator.Create(request, this, ControllerType);
        return instance;
    }
    
    
  3. 取得 IHttpControllerActivator 物件之後,便接著呼叫它的 Create 方法,而此方法會呼叫自己的 GetInstanceOrActivator 方法,以便取得 controller 執行個體。以下程式片段摘自 DefaultHttpControllerActivator 類別的原始碼,我把錯誤處理以及快取機制的部分拿掉,並加上了中文註解:
    // DefaultHttpControllerActivator 類別的 Create 方法(重點摘錄)
    public IHttpController Create(HttpRequestMessage request, 
        HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        Func<IHttpCoontroller> activator;
    
        IHttpController controller = 
            GetInstanceOrActivator(request, controllerType, out activator);
        if (controller != null)
        {
            // 註冊至 Web API 框架的 dependency resolver 
            // 已經建立此 controller 型別的執行個體。
            return controller; // 那就直接使用此物件。
        }
        // 目標 controller 物件尚未建立
        return activator();  // 那就用 GetInstanceOrActivator 方法傳回的委派來建立物件
    }
    
  4. IHttpControllerActivator 物件的 GetInstanceOrActivator 方法會呼叫 HttpRequestMessage 的擴充方法 GetDependencyScope 來取得與當前 request 關聯的 IDependencyScope 物件(其實就是個 Service Locator),並利用它的 GetService 方法來取得 controller 物件。若 GetService 方法並未傳回 controller 物件,而是傳回 null(代表無法解析服務型別),則退而求其次,改用型別反射(reflection)機制來建立 controller 物件。一樣搭配原始碼來看:
    // 摘自 DefaultHttpControllerActivator.cs
    private static IHttpController GetInstanceOrActivator(
        HttpRequestMessage request, Type controllerType, 
        out Func<IHttpCoontroller> activator)
    {
        // 若 dependency scope 有傳回 controller 物件,便使用它。
        IHttpController instance = 
            (IHttpController)request.GetDependencyScope().GetService(controllerType);
        if (instance != null)
        {
            activator = null;
            return instance;
        }
    
        // 否則,建立一個委派來創建此型別的執行個體。
        activator = TypeActivator.Create<IHttpCoontroller>(controllerType);
        return null;
    }
    
    
    其中的 request.GetDependencyScope() 就是對應到剛才說的「呼叫 HttpRequestMessage 的擴充方法 GetDependencyScope 來取得與當前 request 關聯的 IDependencyScope 物件。」而這裡實際取得的 IDependencyScope 物件會是 Web API 框架提供的預設實作:EmptyResolver。從類別名稱可知,這類別其實啥事也沒做——它的 GetService 方法一律傳回 null。因此,在預設情況下,Web API 框架會一律使用型別反射(reflection)機制來建立 controller 物件,而這也就是為什麼我們的 controller 類別一定要有預設建構函式(default constructor)的緣故。

    P.S. 別太擔心 Web API 預設使用 reflection 來建立 controller 物件會影響效能——其中還有型別快取機制,這裡沒提罷了。

(獨白:把這段放進第五章吧!已將本文加入電子書

Post Comments

技術提供:Blogger.