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 物件建立完成為止的過程。
說明(與圖中數字編號對應):
(獨白:把這段放進第五章吧!已將本文加入電子書)
2014-08-01 更新:我把這篇文章整理到《.NET 相依性注入》的第五章時重新校訂了幾次。更精確,也更詳細了,一併更新到這裡。
欲注入相依物件至 controller 類別的建構函式,或從中攔截某些事件來改變預設行為,必須得先了解 Web API 框架建立 controller 的過程。這篇筆記嘗試用活動圖來說明其中的複雜環節。
首先,HTTP request 在進入 Message Handlers 訊息管線之後,行經數個訊息處理常式,然後來到了管線中的最後一站:IHttpControllerDispatcher 物件。這個 IHttpControllerDispatcher 物件收到 HttpRequestMessage 之後,便開始進行 controller 型別的解析,以便建立目標 controller,然後將控制權交給 controller 物件以進行後續處理。
下圖即為 IHttpControllerDispatcher 物件收到 HttpRequestMessage 之後,一直到 controller 物件建立完成為止的過程。
說明(與圖中數字編號對應):
- HttpControllerDispatcher 透過 IHttpControllerSelector 物件的 SelectController 方法來取得目標 controller 型別資訊,這型別資訊是包在一個 HttpControllerDescriptor 物件裡。
- 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; }
- 取得 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 方法傳回的委派來建立物件 }
- IHttpControllerActivator 物件的 GetInstanceOrActivator 方法會呼叫 HttpRequestMessage 的擴充方法 GetDependencyScope 來取得與當前 request 關聯的 IDependencyScope 物件(其實就是個 Service Locator),並利用它的 GetService 方法來取得 controller 物件。若 GetService 方法並未傳回 controller 物件,而是傳回 null(代表無法解析服務型別),則退而求其次,改用型別反射(reflection)機制來建立 controller 物件。一樣搭配原始碼來看:
其中的 request.GetDependencyScope() 就是對應到剛才說的「呼叫 HttpRequestMessage 的擴充方法 GetDependencyScope 來取得與當前 request 關聯的 IDependencyScope 物件。」而這裡實際取得的 IDependencyScope 物件會是 Web API 框架提供的預設實作:EmptyResolver。從類別名稱可知,這類別其實啥事也沒做——它的 GetService 方法一律傳回 null。因此,在預設情況下,Web API 框架會一律使用型別反射(reflection)機制來建立 controller 物件,而這也就是為什麼我們的 controller 類別一定要有預設建構函式(default constructor)的緣故。// 摘自 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; }
P.S. 別太擔心 Web API 預設使用 reflection 來建立 controller 物件會影響效能——其中還有型別快取機制,這裡沒提罷了。
(獨白: