ASP.NET Web API 控制器建立過程

xupengtao7發表於2014-08-18
前言

本來這篇隨筆應該是在上週就該寫出來釋出的,由於身體跟不上節奏感冒發燒有心無力,這種天氣感冒發燒生不如死,也真正的體會到了什麼叫病來如山倒,病去如抽絲。這兩天狀態才好了一點,讓我理解了什麼才是革命的本錢,希望大家也多保重身體。

好了,還是迴歸主題,對於上一篇的內容講解的只是ASP.NET Web API控制器建立過程中的一個區域性知識,在接著上篇內容講解的之前,我會先回顧一下上篇的內容,並且在本篇裡進行整合,讓我們要看到的是一個整個的建立過程。

ASP.NET Web API 控制器建立、啟用過程

l  ASP.NET Web API 控制器建立過程(一)

l  ASP.NET Web API 控制器建立過程(二)

 
建立、啟用過程

圖1

wKioL1PxUgnw1q5JAAO-J5o25OE377.jpg

在前面的篇幅中我們說過APIController是由HttpControllerDispatcher型別來建立的,這只是表面上的,圖1中顯示的就是控制器建立的整個過程了,我們先來回顧一下上一篇所講的,不然會覺得不連貫,在回顧的同時也會對圖1進行講解。

首先我們來分解圖1,可以把圖1中分為兩個部分,

第一個部分就是HttpConfiguration型別所表示的部分。如圖2

圖2

wKiom1PxUQ6BxiG-AAF_rzY-DJ8987.jpg

先來解釋一下HttpConfiguration部分,在HttpConfiguration型別中有兩個屬性,第一個是ServicesContainer型別的屬性Services,第二個就是IDependencyResolver型別的屬性DependencyResolver,對於Services屬性的型別在上篇中我也說過了,就是一個IoC容器,從HttpConfiguration型別角度來看就是一個依賴注入到HttpConfiguration中的IoC容器,對於DependencyResolver屬性來說也差不多就是這個意思了。

只不過Services這個容器中存放的大多都是ASP.NET Web API框架中做一些基礎工作的型別。

就好像上篇中說到的,在ASP.NET Web API框架中載入控制器所在程式集的時候我們就是使用自定義的工作項替換掉了Services容器中的預設工作項:
1
2
    
selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver),
                   newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver());

這裡從圖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表示著對應控制器的控制器描述型別,這個型別很重要稍後再說,我們先要了解ConcurrentDictionary快取的由來。

首先在我們控制器選擇器例項化的時候,在控制器選擇器的建構函式中已經使用了延遲載入技術對控制器快取進行了建立,具體的建立過程可以在圖1看到,是由DefaultAssembliesResolver型別(或者是我們自定義的工作項)載入指定的程式集,並且交由DefaultHttpControllerTypeResolver型別根據ASP.NET Web API框架中預設的搜尋過濾條件返回載入程式集中的所有符合條件的控制器型別(ControllerTypes),來看示例。

所用專案結構還是上個篇幅的示例:

圖3

wKiom1PxUVPDFd0SAAB4Da14LAw080.jpg

圖4

wKiom1PxUV2z2LplAAFRhEA66TI760.jpg

在圖4中我們額外定義了一些控制器型別,然後在SelfHost端定義如下示例程式碼:

程式碼1-1
1
2
3
4
5
6
7
8
9
    
        staticvoidWriterControllerTypeMessage(HttpSelfHostServerselfHostServer)
        {
 
            ICollectiontypes=selfHostServer.Configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(selfHostServer.Configuration.Services.GetAssembliesResolver());
            foreach (Typetypeintypes)
            {
                Console.WriteLine(type.Namespace+"_______"+type.Name);
            }
        }


並且在註冊端呼叫此靜態函式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    
using (HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration))
            {
                selfHostServer.Configuration.Routes.MapHttpRoute(
                    "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional });
 
                selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver),
                    newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver());
 
 
                WriterControllerTypeMessage(selfHostServer);
 
                selfHostServer.OpenAsync();
                Console.WriteLine("伺服器端服務監聽已開啟");
                Console.Read();
            }


結果如圖5:

圖5

wKiom1PxUXXDmUPEAAEjcnAu8us361.jpg

在我們獲取了ControllerTypes過後了,ASP.NET Web API框架中有個HttpControllerTypeCache型別的物件就藏不住了,之前的一些操作都是由HttpControllerTypeCache型別去處理的,而在HttpControllerTypeCache獲取了ControllerTypes過後就要做一個很重要的工作了,就是對ControllerTypes進行分組操作最後返回一個Dictionary>型別的物件,就拿上面的示例來說吧,最後經過分組後的Dictionary>型別值應該是:

Writer--&gtNameSpaceControllerOne->WriterController

      NameSpaceControllerTwo->WriterController

Read--&gtNameSpaceControllerOne->ReadController

WriterAndRead--&gtNameSpaceControllerThree->WriterAndReadController

Product--&gtWebAPIController->ProductController

這個時候的值並不是最終的快取型別,而是透過我們的控制器選擇器根據HttpControllerTypeCache型別所生成的Dictionary>型別值來生成ConcurrentDictionary快取型別,還是根據上面的示例,我們看一下最後生成的快取型別值。

修改1-1如下示例程式碼:

程式碼1-2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    
staticvoidWriterControllerTypeMessage(HttpSelfHostServerselfHostServer)
        {
 
            ICollectiontypes=selfHostServer.Configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(selfHostServer.Configuration.Services.GetAssembliesResolver());
            foreach (Typetypeintypes)
            {
                Console.WriteLine(type.Namespace+"_______"+type.Name);
            }
 
 
 
            //Dictionary> controllertypecache = types.GroupBy(t => t.Name,StringComparer.OrdinalIgnoreCase).ToDictionary, string, ILookup>
            //        (g => g.Key,
            //        g => g.ToLookup(t => (t.Namespace ?? string.Empty),StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase);
 
            //foreach(var value in controllertypecache)
            //{
            //    foreach (var val in value.Value)
            //    {
                    
            //    }
            //}
 
            IDictionarymapping=selfHostServer.Configuration.Services.GetHttpControllerSelector().GetControllerMapping();
 
            foreach (varmeginmapping)
            {
                Console.WriteLine("ControllerName:"+meg.Key+".ControllerTypeName:"+meg.Value.ControllerType.Name);
            }
 
        }


結果如圖6:

圖6

wKiom1PxUYzjIU-aAAGVFu9zCOw938.jpg

(在程式碼1-2中註釋掉的部分就是可以檢視對ControllerTypes進行分組操作返回Dictionary>型別的值)。

控制器選擇器主要功能

次要功能看完之後,主要功能想必大家也是很明瞭吧,在有了控制器快取物件過後,控制器選擇器則會根據HttpRequestMessage物件中的路由資料物件獲取控制器名稱,然後從快取中獲取到對應的HttpControllerDescriptor型別例項。

 

具體生成工作

在獲取到了HttpControllerDescriptor型別例項過後生成IHttpController的工作就變得很簡單了,還是從HttpConfiguration中的Services容器中獲得對應的負責控制器生成啟用的工作項,在圖1中可以明確的看出是DefaultHttpControllerActivator型別,在DefaultHttpControllerActivator型別工作的時候它會從HttpConfiguration中獲取DependencyResolver屬性對應的容器,如果這裡的情況不滿足才會呼叫後面的TypeActivator來生成啟用IHttpController(透過反射)。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28904215/viewspace-1252970/,如需轉載,請註明出處,否則將追究法律責任。

相關文章