標籤:des style blog http color 使用 os io
先看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 響應訊息。訊息處理流程如下:
- 當用戶端對伺服器發出的 HTTP 要求開始進入 ASP.NET Web API 架構時,該 HTTP 要求訊息會被封裝成HttpRequestMessage對象,並且進入圖中最頂端「裝載」方塊的HttpServer(web 裝載)或HttpSelfHostServer(自我裝載)。接著該訊息便流入管線的下一個階段,直到整個訊息流程處理完畢,會得到一個代表 HTTP 響應訊息的 HttpResponseMessage對象,並將此對象的訊息內容傳回用戶端。
- HttpRequestMessage對象進入「訊息處理常式」管線。在此階段,HTTP 訊息行經數個訊息處理常式(message handlers),並且在返回 HTTP 響應訊息時以相反的順序執行。
- 在各個訊息處理常式之後,HTTP 要求訊息接著會傳遞給HttpControllerDispatcher,並且由這個對象來建立 Web API controller,然後將 HTTP 要求傳遞給 controller 對象(圖中標示「(A) 建立 controller」的步驟)。
- Controller 會先決定目標動作方法(即圖中標示「(B) 選擇 action」的步驟),然後呼叫它。動作方法將負責產生響應內容,之後便依前述管線流程的反方向沿路返回。
以上便是 Web API HTTP 訊息管線的大致處理流程。
Web API Controller 是怎樣建成的?
剛才只說明了 Web API HTTP 訊息管線的大致處理流程,而欲注入相依對象至 controller 類別的建構函式,或從中動些手腳來改變預設行為,必得瞭解 Web API 架構建立 controller 的內部過程。本節將進一步說明其中的複雜環節,其中會反覆提及多個抽象介面,第一次閱讀時可能略感吃力,並難免心生疑惑,但等到實際寫過、跑過一遍後面的範常式序,再回頭來看這一節的說明,整個拼圖應該就會漸漸明朗了。
剛才提到,HttpControllerDispatcher會建立目標 controller 對象,亦即先前 ASP.NET Web
API 管線架構圖中標示「(A) 建立 controller」的步驟。此步驟其實包含兩件工作:
- 解析目標 controller。亦即決定該使用哪一個 controller 類別。
- 建立目標 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.csprivate 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 章。