ASP.NET Web API 控制器建立過程(二)
前言
本來這篇随筆應該是在上周就該寫出來釋出的,由于身體跟不上節奏感冒發燒有心無力,這種天氣感冒發燒生不如死,也真正的體會到了什麼叫病來如山倒,病去如抽絲。這兩天狀态才好了一點,讓我了解了什麼才是革命的本錢,希望大家也多保重身體。
好了,還是回歸主題,對于上一篇的内容講解的隻是ASP.NET Web API控制器建立過程中的一個局部知識,在接着上篇内容講解的之前,我會先回顧一下上篇的内容,并且在本篇裡進行整合,讓我們要看到的是一個整個的建立過程。
ASP.NET Web API 控制器建立、激活過程
l ASP.NET Web API 控制器建立過程(一)
l ASP.NET Web API 控制器建立過程(二)
圖1
<a href="http://s3.51cto.com/wyfs02/M00/46/43/wKioL1PxUgnw1q5JAAO-J5o25OE377.jpg" target="_blank"></a>
在前面的篇幅中我們說過APIController是由HttpControllerDispatcher類型來建立的,這隻是表面上的,圖1中顯示的就是控制器建立的整個過程了,我們先來回顧一下上一篇所講的,不然會覺得不連貫,在回顧的同時也會對圖1進行講解。
首先我們來分解圖1,可以把圖1中分為兩個部分,
第一個部分就是HttpConfiguration類型所表示的部分。如圖2
圖2
<a href="http://s3.51cto.com/wyfs02/M02/46/41/wKiom1PxUQ6BxiG-AAF_rzY-DJ8987.jpg" target="_blank"></a>
先來解釋一下HttpConfiguration部分,在HttpConfiguration類型中有兩個屬性,第一個是ServicesContainer類型的屬性Services,第二個就是IDependencyResolver類型的屬性DependencyResolver,對于Services屬性的類型在上篇中我也說過了,就是一個IoC容器,從HttpConfiguration類型角度來看就是一個依賴注入到HttpConfiguration中的IoC容器,對于DependencyResolver屬性來說也差不多就是這個意思了。
隻不過Services這個容器中存放的大多都是ASP.NET Web API架構中做一些基礎工作的類型。
就好像上篇中說到的,在ASP.NET Web API架構中加載控制器所在程式集的時候我們就是使用自定義的工作項替換掉了Services容器中的預設工作項:
1
2
<code>selfHostServer.Configuration.Services.Replace(</code><code>typeof</code><code>(IAssembliesResolver),</code>
<code> </code><code>newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver());</code>
這裡從圖2中可以看出預設的DefaultAssembliesResolver類型來執行這項工作的。
到這裡也就是上個篇幅中的主要内容了。下面我們還是繼續分解圖1,上面說了第一部分了下面來看第二部分,第二個部分就是HttpControllerDispatcher類型到APIController類型的生成過程,也就是圖1了。
首先我們的ASP.NET Web API架構會從HttpConfiguration中的Services容器中擷取一個ControllerSelector(控制器選擇器),這個控制器選擇器呢對應的類型大家從圖2中也可以看到,圖1中也有,很明了。
那麼ControllerSelector主要幹什麼呢?肯定是選擇控制器阿,當然了根據請求選擇相應的控制器是主要功能,次要功能是啥?次要功能是生成控制器緩存,不然從哪選阿對不。在ASP.NET MVC架構中控制器緩存是存在xml檔案中的,現在很好奇在ASP.NET Web API架構中控制器緩存是什麼樣的存儲方式呢?
我們就來看一下控制器選擇器的次要功能。
控制器選擇器次要功能
首先我們先說明一下緩存的類型為ConcurrentDictionary<string, HttpControllerDescriptor>類型,就是一個一一對應的鍵值隊,string表示着控制器名稱,而HttpControllerDescriptor表示着對應控制器的控制器描述類型,這個類型很重要稍後再說,我們先要了解ConcurrentDictionary<string, HttpControllerDescriptor>緩存的由來。
首先在我們控制器選擇器執行個體化的時候,在控制器選擇器的構造函數中已經使用了延遲加載技術對控制器緩存進行了建立,具體的建立過程可以在圖1看到,是由DefaultAssembliesResolver類型(或者是我們自定義的工作項)加載指定的程式集,并且交由DefaultHttpControllerTypeResolver類型根據ASP.NET Web API架構中預設的搜尋過濾條件傳回加載程式集中的所有符合條件的控制器類型(ControllerTypes),來看示例。
所用項目結構還是上個篇幅的示例:
圖3
<a href="http://s3.51cto.com/wyfs02/M00/46/41/wKiom1PxUVPDFd0SAAB4Da14LAw080.jpg" target="_blank"></a>
圖4
<a href="http://s3.51cto.com/wyfs02/M01/46/41/wKiom1PxUV2z2LplAAFRhEA66TI760.jpg" target="_blank"></a>
在圖4中我們額外定義了一些控制器類型,然後在SelfHost端定義如下示例代碼:
代碼1-1
3
4
5
6
7
8
9
<code> </code><code>staticvoidWriterControllerTypeMessage(HttpSelfHostServerselfHostServer)</code>
<code> </code><code>{</code>
<code> </code>
<code> </code><code>ICollection<Type>types=selfHostServer.Configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(selfHostServer.Configuration.Services.GetAssembliesResolver());</code>
<code> </code><code>foreach</code> <code>(Typetypeintypes)</code>
<code> </code><code>{</code>
<code> </code><code>Console.WriteLine(type.Namespace+</code><code>"_______"</code><code>+type.Name);</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
并且在注冊端調用此靜态函數:
10
11
12
13
14
15
<code>using</code> <code>(HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration))</code>
<code> </code><code>selfHostServer.Configuration.Routes.MapHttpRoute(</code>
<code> </code><code>"DefaultApi"</code><code>, </code><code>"api/{controller}/{id}"</code><code>, </code><code>new</code> <code>{ id=RouteParameter.Optional });</code>
<code> </code><code>selfHostServer.Configuration.Services.Replace(</code><code>typeof</code><code>(IAssembliesResolver),</code>
<code> </code><code>newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver());</code>
<code> </code><code>WriterControllerTypeMessage(selfHostServer);</code>
<code> </code><code>selfHostServer.OpenAsync();</code>
<code> </code><code>Console.WriteLine(</code><code>"伺服器端服務監聽已開啟"</code><code>);</code>
<code> </code><code>Console.Read();</code>
結果如圖5:
圖5
<a href="http://s3.51cto.com/wyfs02/M01/46/41/wKiom1PxUXXDmUPEAAEjcnAu8us361.jpg" target="_blank"></a>
在我們擷取了ControllerTypes過後了,ASP.NET Web API架構中有個HttpControllerTypeCache類型的對象就藏不住了,之前的一些操作都是由HttpControllerTypeCache類型去處理的,而在HttpControllerTypeCache擷取了ControllerTypes過後就要做一個很重要的工作了,就是對ControllerTypes進行分組操作最後傳回一個Dictionary<string, ILookup<string, Type>>類型的對象,就拿上面的示例來說吧,最後經過分組後的Dictionary<string, ILookup<string, Type>>類型值應該是:
Writer-->NameSpaceControllerOne->WriterController
NameSpaceControllerTwo->WriterController
Read-->NameSpaceControllerOne->ReadController
WriterAndRead-->NameSpaceControllerThree->WriterAndReadController
Product-->WebAPIController->ProductController
這個時候的值并不是最終的緩存類型,而是通過我們的控制器選擇器根據HttpControllerTypeCache類型所生成的Dictionary<string,ILookup<string, Type>>類型值來生成ConcurrentDictionary<string, HttpControllerDescriptor>緩存類型,還是根據上面的示例,我們看一下最後生成的緩存類型值。
修改1-1如下示例代碼:
代碼1-2
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<code>staticvoidWriterControllerTypeMessage(HttpSelfHostServerselfHostServer)</code>
<code> </code><code>//Dictionary<string,ILookup<string, Type>> controllertypecache = types.GroupBy<Type,string>(t => t.Name,StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string,Type>, string, ILookup<string, Type>></code>
<code> </code><code>// (g => g.Key,</code>
<code> </code><code>// g => g.ToLookup<Type,string>(t => (t.Namespace ?? string.Empty),StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase);</code>
<code> </code><code>//foreach(var value in controllertypecache)</code>
<code> </code><code>//{</code>
<code> </code><code>// foreach (var val in value.Value)</code>
<code> </code><code>// {</code>
<code> </code>
<code> </code><code>// }</code>
<code> </code><code>//}</code>
<code> </code><code>IDictionary<</code><code>string</code><code>, HttpControllerDescriptor>mapping=selfHostServer.Configuration.Services.GetHttpControllerSelector().GetControllerMapping();</code>
<code> </code><code>foreach</code> <code>(varmeginmapping)</code>
<code> </code><code>Console.WriteLine(</code><code>"ControllerName:"</code><code>+meg.Key+</code><code>".ControllerTypeName:"</code><code>+meg.Value.ControllerType.Name);</code>
結果如圖6:
圖6
<a href="http://s3.51cto.com/wyfs02/M02/46/41/wKiom1PxUYzjIU-aAAGVFu9zCOw938.jpg" target="_blank"></a>
(在代碼1-2中注釋掉的部分就是可以檢視對ControllerTypes進行分組操作傳回Dictionary<string, ILookup<string, Type>>類型的值)。
控制器選擇器主要功能
次要功能看完之後,主要功能想必大家也是很明了吧,在有了控制器緩存對象過後,控制器選擇器則會根據HttpRequestMessage對象中的路由資料對象擷取控制器名稱,然後從緩存中擷取到對應的HttpControllerDescriptor類型執行個體。
具體生成工作
在擷取到了HttpControllerDescriptor類型執行個體過後生成IHttpController的工作就變得很簡單了,還是從HttpConfiguration中的Services容器中獲得對應的負責控制器生成激活的工作項,在圖1中可以明确的看出是DefaultHttpControllerActivator類型,在DefaultHttpControllerActivator類型工作的時候它會從HttpConfiguration中擷取DependencyResolver屬性對應的容器,如果這裡的情況不滿足才會調用後面的TypeActivator來生成激活IHttpController(通過反射)。
本文轉自jinyuan0829 51CTO部落格,原文連結:http://blog.51cto.com/jinyuan/1541436,如需轉載請自行聯系原作者