ASP.NET Web API Controller 是怎么建成的


先看ASP.NET Web API 訊息管線:

註:為了避免圖片太大以至於超過版面,上圖中的「HTTP 訊息處理程序」區塊省略了 HttpRoutingDispatcher 處理路由分派的部分。「控制器」區塊則省略了篩選條件(filter)的處理細節。微軟網站有提供一份比較完整的 Web API 訊息處理流程圖,網址是 http://www.microsoft.com/en-us/download/details.aspx?id=36476

 

此訊息管線架構圖分為三層,由上至下,分別是裝載(Hosting)、訊息處理程序(Message Handlers)、以及控制器(Controller)。圖中的紅色實心箭頭代表 HTTP 請求訊息,虛線箭頭代表 HTTP 響應消息。訊息處理流程如下:

  1. 當客戶端對服務器發出的 HTTP 請求開始進入 ASP.NET Web API 框架時,該 HTTP 請求訊息會被包裝成HttpRequestMessage對象,並且進入圖中最頂端「裝載」方塊的HttpServer(web 裝載)或HttpSelfHostServer(自我裝載)。接着該訊息便流入管線的下一個階段,直到整個訊息流程處理完畢,會得到一個代表 HTTP 響應消息的 HttpResponseMessage對象,並將此對象的訊息內容傳回客戶端。
  2. HttpRequestMessage對象進入「訊息處理程序」管線。在此階段,HTTP 訊息行經數個訊息處理程序(message handlers),並且在返回 HTTP 響應消息時以相反的順序執行。
  3. 在各個訊息處理程序之后,HTTP 請求訊息接着會傳遞給HttpControllerDispatcher,並且由這個對象來建立 Web API controller,然后將 HTTP 請求傳遞給 controller 對象(圖中標示「(A) 建立 controller」的步驟)。
  4. Controller 會先決定目標動作方法(即圖中標示「(B) 選擇 action」的步驟),然后呼叫它。動作方法將負責產生響應內容,之后便依前述管線流程的反方向沿路返回。

以上便是 Web API HTTP 訊息管線的大致處理流程。

 

Web API Controller 是怎樣建成的?

剛才只說明了 Web API HTTP 訊息管線的大致處理流程,而欲注入相依對象至 controller 類別的建構函式,或從中動些手腳來改變預設行為,必得了解 Web API 框架建立 controller 的內部過程。本節將進一步說明其中的復雜環節,其中會反復提及多個抽象接口,第一次閱讀時可能略感吃力,並難免心生疑惑,但等到實際寫過、跑過一遍后面的范例程序,再回頭來看這一節的說明,整個拼圖應該就會漸漸明朗了。

剛才提到,HttpControllerDispatcher會建立目標 controller 對象,亦即先前 ASP.NET Web

API 管線架構圖中標示「(A) 建立 controller」的步驟。此步驟其實包含兩件工作:

  1. 解析目標 controller。亦即決定該使用哪一個 controller 類別。
  2. 建立目標 controller 類別的實例,並將 HTTP 請求(HttpRequestMessage對象)傳遞給它,以便由 controller 進行后續處理。

首先,「解析目標 controller」的工作主要是從應用程序的 DLL 組件中尋找所有可用的 controller 類別,再從中選擇一個與當前 HTTP request 匹配的。其處理邏輯如下圖所示:

說明:

  • 圖中下方的IAssembliesResolver對象的GetAssemblies方法將提供應用程序的組件列表,並由IHttpControllerTypeResolver對象的GetControllerTypes方法取得可用的 controller 類別清單。
  • IHttpControllerSelector負責決定要選擇哪一個 controller 類別,然后返回一個包含其型別信息的HttpControllerDescriptor對象給HttpControllerDispatcher。

 從確定目標 controller 型別之后,到建立完成 controller 實例的過程中,還有經過一些核心標准接口所提供的擴充點。底下再用一張 UML 活動圖搭配 Web API 原始碼的方式來解構其內部處理過程。

說明如下(與上圖中的數字編號對應):

(1) HttpControllerDispatcher透過IHttpControllerSelector對象的SelectController方法來取得目標 controller 型別信息,這型別信息是包在一個HttpControllerDescriptor 對象里。

(2) HttpControllerDispatcher接着呼叫HttpControllerDescriptor對象的CreateController 方法,而該方法又會去呼叫ServicesContainer對象的GetHttpControllerActivator方法來取得IHttpControllerActivator對象。以下程序片段摘自 Web API 原始碼,涵蓋了此步驟至下一步驟的部分邏輯:

// HttpControllerDescriptor 類別的 CreateController 方法。
public virtual IHttpController CreateController(HttpRequestMessage request) 
{
    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<IHttpController> 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<IHttpController>> activator)
{
   // 若 dependency scope 有傳回 controller 對象,便使用它。
   IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType);
   if (instance != null)
   {
      activator = null;
      return instance;
   }

   // 否則,建立一個委派來創建此型別的實例。
   activator = TypeActivator.Create<IHttpController>(controllerType);
   return null;
}

其中的request.GetDependencyScope()就是對應到剛才說的「呼叫HttpRequestMessage 的擴充方法 GetDependencyScope 來取得與當前 request 關聯的 IDependencyScope 對象。」而這里實際取得的IDependencyScope對象會是 Web API 框架提供的預設實作: EmptyResolver。從類別名稱可知,這類別其實啥事也沒做——它的GetService方法一律傳回null。因此,在預設情況下,Web API 框架會一律使用型別反射(reflection)機制來建立 controller 對象,而這也就是為什么我們的 controller 類別一定要有預設建構函式(default constructor)的緣故。

大致上,controller 對象就是這么建成的。


本文摘自《.NET 相依性注入》一書的第 5 章。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM