文章出處

Web API調用請求的目標是定義在某個HttpController類型中的某個Action方法,所以消息處理管道最終需要激活目標HttpController對象。調用請求的URI會攜帶目標HttpController的名稱,該名稱經過路由解析之后會作為路由變量保存到一個HttpRouteData對象中,而后者會被添加到代表當前請求的HttpRequestMessage對象的屬性字典中。ASP.NET Web API據此解析出目標HttpController的類型,進而實現針對目標HttpController實例的激活。[本文已經同步到《How ASP.NET Web API Works?》]

目錄
一、程序集的解析
二、HttpController類型的解析
    1、DefaultHttpControllerTypeResolver
    2、HttpController類型的緩存
三、HttpController的選擇
    1、DefaultHttpControllerSelector
    2、獲取目標HttpController的名稱
    3、建立HttpController名稱與HttpControllerDescriptor之間的映射
    4、根據請求選擇HttpController
四、HttpController的創建
    1、HttpControllerActivator
    2、DefaultHttpControllerActivator
    3、DependencyResolver
    4、HttpRequestMessage中的DependencyResolver
    5、DependencyResolver在DefaultHttpControllerActivator中的應用

一、程序集的解析

在ASP.NET Web API的HttpController激活系統中,AssembliesResolver為目標HttpController類型解析提供候選的程序集。換句話說,候選HttpController類型的選擇范圍僅限于定義在由AssembliesResolver提供的程序集中的所有實現了IHttpController接口的類型。所有的AssembliesResolver均實現了接口IAssembliesResolver,該接口定義在命名空間“System.Web.Http.Dispatcher”下,如果未作特別說明,本節新引入的類型均定義在此命名空間下。如下面的代碼片斷所示,IAssembliesResolver接口中僅僅定義了一個唯一的GetAssemblies方法,該方法返回的正是提供的程序集列表。

   1: public interface IAssembliesResolver
   2: {
   3:     ICollection<Assembly> GetAssemblies();
   4: }

默認使用的AssembliesResolver類型為DefaultAssembliesResolver。如下面的代碼片斷所示,DefaultAssembliesResolver在實現的GetAssemblies方法中直接返回當前應用程序域加載的所有程序集列表。

   1: public class DefaultAssembliesResolver : IAssembliesResolver
   2: {
   3:     public virtual ICollection<Assembly> GetAssemblies()
   4:     {
   5:         return AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>();
   6:     }
   7: }

DefaultAssembliesResolver是默認使用的AssembliesResolver,那么默認的AssembliesResolver類型在ASP.NET Web API是如何確定的呢?要回答這個問題,需要涉及到另一個重要的類型ServicesContainer,它定義在命名空間“System.Web.Http.Controllers”下。

由于DefaultAssembliesResolver在為HttpController類型解析提供的程序集僅限于當前應用程序域已經加載的程序集,如果目標HttpController定義在尚未加載的程序集中,我們不得不預先加載它們。但是這樣的問題只會發生在Self Host寄宿模式下,如果采用Web Host寄宿模式則無此困擾,原因在于后者默認使用的是另一個AssembliesResolver類型。我們知道在Web Host寄宿模式下用于配置ASP.NET Web API消息處理管道的是通過類型GlobalConfiguration的靜態只讀屬性Configuration返回的HttpConfiguration對象。從如下的代碼片斷我們可以發現,當GlobalConfiguration的Configuration屬性被第一次訪問的時候,在ServicesContainer中注冊的AssembliesResolver會被替換成一個類型為WebHostAssembliesResolver的對象。

   1: public static class GlobalConfiguration
   2: {
   3:     //其他成員
   4:     static GlobalConfiguration()
   5:     {
   6:         _configuration = new Lazy<HttpConfiguration>(delegate 
   7:            {
   8:                HttpConfiguration configuration = new HttpConfiguration( new HostedHttpRouteCollection(RouteTable.Routes));
   9:                configuration.Services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver());        
  10:                //其他操作
  11:                 return configuration;
  12:            });
  13:       //其他操作
  14:      }
  15:  
  16:      public static HttpConfiguration Configuration
  17:   {
  18:       get
  19:       {
  20:           return _configuration.Value;
  21:       }
  22:   }
  23: }

WebHostAssembliesResolver是一個定義在程序集“System.Web.Http.WebHost.dll”中的內部類型。從如下的代碼片斷可以看出WebHostAssembliesResolver在實現的GetAssemblies方法中直接通過調用BuildManager的GetReferencedAssemblies方法來獲取最終提供的程序集。

   1: internal sealed class WebHostAssembliesResolver : IAssembliesResolver
   2: {
   3:     ICollection<Assembly> IAssembliesResolver.GetAssemblies()
   4:     {
   5:         return BuildManager.GetReferencedAssemblies().OfType<Assembly>().ToList<Assembly>();
   6:     }
   7: }

由于BuildManager的GetReferencedAssemblies方法幾乎返回了在運行過程中需要的所有程序集,如果我們將HttpController類型定義在單獨的程序集中,我們只要確保該程序集已經正常部屬就可以了。如果有人對此感興趣,可以試著將上面演示的實例從Self Host寄宿模式轉換成Web Host寄宿模式,看看ASP.NET Web API的HttpController激活系統能否正常解析出分別定義在Foo.dll、Bar.dll和Baz.dll中的HttpController類型。

二、HttpController類型的解析

注冊在當前ServicesContainer上的AssembliesResolver對象為HttpController類型的解析提供了可供選擇的程序集,真正用于解析HttpController類型的是一個名為HttpControllerTypeResolver的對象。所有的HttpControllerTypeResolver類型均實現了接口IHttpControllerTypeResolver,如下面的代碼片斷所示,定義其中的唯一方法GetControllerTypes借助于提供的AssembliesResolver解析出所有的HttpController類型。

   1: public interface IHttpControllerTypeResolver
   2: {
   3:     ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver);
   4: }

與AssembliesResolver注冊方式類似,默認使用的HttpControllerTypeResolver同樣是注冊在當前HttpConfiguration的ServicesContainer對象上。我們可以通過ServicesContainer具有如下定義的擴展方法GetHttpControllerTypeResolver得到這個注冊的HttpControllerTypeResolver對象。

   1: public static class ServicesExtensions
   2: {
   3:     //其他成員
   4:     public static IHttpControllerTypeResolver GetHttpControllerTypeResolver(this ServicesContainer services);
   5: }

我們同樣可以通過HttpConfiguration默認采用的DefaultServices的構造函數得到默認注冊的HttpControllerTypeResolver對象的類型。如下面的代碼片斷所示,這個默認注冊的HttpControllerTypeResolver是一個類型為DefaultHttpControllerTypeResolver的對象。

   1: public class DefaultServices : ServicesContainer
   2: {
   3:     //其他成員
   4:     public DefaultServices(HttpConfiguration configuration)
   5:     {
   6:         //其他操作
   7:         this.SetSingle<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver());
   8:     }
   9: }

1、DefaultHttpControllerTypeResolver

如下面的代碼片斷所示, DefaultHttpControllerTypeResolver具有一個Predicate<Type>類型的只讀屬性IsControllerTypePredicate,返回的委托對象用于判斷指定的類型是否是一個有效的HttpController類型。

   1: public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
   2: {
   3:     public DefaultHttpControllerTypeResolver();
   4:     public DefaultHttpControllerTypeResolver(Predicate<Type> predicate);
   5:  
   6:     public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver);
   7:  
   8:     protected Predicate<Type> IsControllerTypePredicate { get; }
   9: }

如果我們具有特別的HttpController類型有效性驗證規則,可以在調用構造函數實例化DefaultHttpControllerTypeResolver對象時通過參數指定這個Predicate<Type>委托對象。在默認情況下,這個自動初始化的Predicate<Type>對象體現了默認采用的HttpController類型有效驗證規則。具體來說,默認情況下一個給定的類型必須同時滿足如下的條件才是一個有效的HttpController類型。

  • 是一個外部可見(IsVisible = true)的實例(IsAbstract = false)類(IsClass = true)。
  • 類型直接或者間接實現了接口IHttpController。
  • 類型名稱必須以“Controller”為后綴,但是不區分大小寫(可以使用“controller”作為后綴)。

用于提供所有有效HttpController類型的GetControllerTypes方法的實現邏輯其實很簡單。它通過指定的AssembliesResolver得到一個程序集列表,對于定義在這些程序集中的所有類型,如果滿足上述的要求就是返回的HttpController類型之一。定義在類型DefaultHttpControllerTypeResolver中的針對有效HttpController類型的解析邏輯基本上體現在如下所示的代碼中。

   1: public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
   2: {
   3:     //其他成員
   4:     public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver)
   5:     {
   6:         List<Type> types = new List<Type>();
   7:         foreach (Assembly assembly in assembliesResolver.GetAssemblies())
   8:         {
   9:             foreach (Type type in assembly.GetTypes())
  10:             {
  11:                 if (this.IsControllerTypePredicate(type))
  12:                 {
  13:                     types.Add(type);
  14:                 }
  15:             }
  16:         }
  17:         return types;
  18:     }
  19: }

2、HttpController類型的緩存

由于針對所有HttpController類型的解析需要大量使用到反射,這是一個相對耗時的過程,所以ASP.NET Web API會對解析出來的HttpController類型進行緩存。具體的緩存實現在具有如下定義的HttpControllerTypeCache類型中,這是一個定義在程序集“System.Web.Http.dll”中的內部類型。

   1: internal sealed class HttpControllerTypeCache
   2: {
   3:     //其他成員   
   4:     internal Dictionary<string, ILookup<string, Type>> Cache { get; }
   5: }

緩存的HttpController類型通過只讀屬性Cache獲取,這是一個類型為Dictionary<string, ILookup<string, Type>>的字典對象。該字典的Key表示HttpController的名稱(HttpController類型名稱去除“Controller”后綴),其Value返回的ILookup<string, Type>對象包含一組具有相同名稱的HttpController類型列表,自身的Key表示HttpController類型的命名空間。

三、目標HttpController的選擇

AssembliesResolver僅僅是將所有合法的HttpController類型解析出來,針對具體的調用請求,系統必須從中選擇一個與當前請求匹配的HttpController類型出來。HttpController的選擇通過HttpControllerSelector對象來完成,所有的HttpControllerSelector類型均實現了具有如下定義的接口IHttpControllerSelector。

   1: public interface IHttpControllerSelector
   2: {
   3:     IDictionary<string, HttpControllerDescriptor> GetControllerMapping();
   4:     HttpControllerDescriptor SelectController(HttpRequestMessage request);
   5: }

如上面的代碼片斷所示,該接口中定義了GetControllerMapping和SelectController兩個方法。GetControllerMapping返回一個描述所有HttpController類型的HttpControllerDescriptor對象與對應的HttpController名稱之間的映射關系。針對請求的HttpController選擇實現在SelectController方法中,它返回描述目標HttpController的HttpControllerDescriptor對象。

1、DefaultHttpControllerSelector

默認使用HttpControllerSelector依然注冊到當前的ServicesContainer對象中,我們可以調用ServicesContainer如下所示的擴展方法GetHttpControllerSelector得到注冊的HttpControllerSelector對象。

   1: public static class ServicesExtensions
   2: {
   3:     //其他成員
   4:     public static IHttpControllerSelector GetHttpControllerSelector(this ServicesContainer services);
   5: }

如下的代碼片斷所示,默認使用的DefaultServices在初始化的過程中會根據指定的HttpConfiguration對象創建一個DefaultHttpControllerSelector對象,并將其注冊為默認的HttpControllerSelector。

   1: public class DefaultServices : ServicesContainer
   2: {
   3:     //其他成員
   4:     public DefaultServices(HttpConfiguration configuration)
   5:     {
   6:         //其他操作
   7:         this.SetSingle<IHttpControllerSelector>(new DefaultHttpControllerSelector(configuration));
   8:     }
   9: }

如下面的代碼片斷所示,DefaultHttpControllerSelector不僅僅實現了IHttpControllerSelector接口中定義的兩個方法,還定義了另一個名為GetControllerName方法,我們可以調用此方法根據指定HttpRequestMessage對象得到該請求訪問的目標HttpController的名稱。

   1: public class DefaultHttpControllerSelector : IHttpControllerSelector
   2: {
   3:     public DefaultHttpControllerSelector(HttpConfiguration configuration);
   4:  
   5:     public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping();
   6:     public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request);
   7:  
   8:     public virtual string GetControllerName(HttpRequestMessage request);
   9: }

2、獲取目標HttpController的名稱

如果采用Web Host寄宿模式,消息管道的締造者HttpControllerHandler在根據當前HTTP上下文創建用于表示請求的HttpRequestMessage對象后,會將ASP.NET路由系統解析當前請求得到的RouteData對象轉換成HttpRouteData對象并添加到HttpRequestMessage的屬性字典中。對于Self Host寄宿模式來說,處于消息處理管道末端的HttpRoutingDispatcher會利用ASP.NET Web API的路由系統對當前請求進行路由解析并直接得到封裝了路由數據的HttpRouteData對象,此HttpRouteData同樣會被添加到表示當前請求的HttpRequestMessage對象的屬性字典之中。

由于被附加到當前請求的HttpRouteData已經包含了目標HttpController的名稱(對應的變量名為“controller”),所以我們可以從HttpRequestMessage中直接獲取目標HttpController的名稱。如下面的代碼片斷所示,DefaultHttpControllerSelector的GetControllerName方法也是按照這樣的邏輯從指定的HttpMessageMessage中提取目標HttpController的名稱。

   1: public class DefaultHttpControllerSelector : IHttpControllerSelector
   2: {  
   3:     //其他成員
   4:     public virtual string GetControllerName(HttpRequestMessage request)
   5:     {      
   6:         IHttpRouteData routeData = request.GetRouteData();
   7:         if (routeData == null)
   8:         {
   9:             return null;
  10:         }
  11:         string str = null;
  12:         routeData.Values.TryGetValue<string>("controller", out str);
  13:         return str;
  14:     }    
  15: }

3、建立HttpController名稱與HttpControllerDescriptor之間的映射

DefaultHttpControllerSelector 的GetControllerMapping方法會返回一個類型為IDictionary<string, HttpControllerDescriptor>的字典,它包含了描述所有HttpController的HttpControllerDescriptor對象與對應HttpController名稱之間的映射關系。

   1: public class DefaultHttpControllerSelector : IHttpControllerSelector
   2: {
   3:     //其他成員
   4:     private readonly HttpControllerTypeCache _controllerTypeCache;
   5:     public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping();
   6: }

GetControllerMapping方法的實現邏輯其實很簡單。如上面的代碼片斷所示,DefaultHttpControllerSelector具有一個HttpControllerTypeCache類型的只讀字段,通過它可以得到HttpController類型與名稱之間的關系,GetControllerMapping方法只需要根據HttpController類型生成對應的HttpControllerDescriptor對象即可。但是有個問題必須要考慮,由于同名的HttpController類型可能定義在不同的命名空間下,而且這里所指的“HttpController名稱”是不區分大小寫的,所以一個HttpController名稱可能對應著多個HttpController類型,這也是為何HttpControllerTypeCache緩存的數據是一個類型為Dictionary<string, ILookup<string, Type>>的字典對象的原因。

4、根據請求選擇HttpController

其實HttpControllerSelector的終極目標還是根據請求實現對目標HttpController的選擇,這體現在它的SelectController方法上。對于默認注冊的DefaultHttpControllerSelector來說,其SelectController方法的實現邏輯非常簡單,它只需要調用GetControllerName方法從給定的HttpRequestMessage提取目標HttpController的名稱,然后根據此名稱從GetControllerMapping方法的返回值中提取對應的HttpControllerDescriptor對象即可。實現在SelectController方法中針對請求的HttpController選擇機制雖然簡單,但是針對幾種特殊情況的處理機制我們不應該忽視。

首先,如果調用GetControllerName方法返回的HttpController名稱為Null或者是一個空字符串,意味著ASP.NET路由系統(針對Web Host寄宿模式)或者ASP.NET Web API路由系統(針對Self Host寄宿模式)在對請求的解析過程中并沒有得到表示目標HttpController名稱的路由變量。這種情況下DefaultHttpControllerSelector會直接拋出一個響應狀態為HttpStatusCode.NotFound的HttpResponseException異常,客戶端自然就會接收到一個狀態為“404, Not Found”的響應。

其次,如果在調用GetControllerMapping方法返回的字典中并沒有一個與目標HttpController名稱相匹配的HttpControllerDescriptor對象,通過上面的分析我們知道如下兩種情況會導致這樣的問題。

  • 在通過AssembliesResolver提供的程序集中并不曾定義這么一個有效的HttpController類型。
  • 在通過AssembliesResolver提供的程序集中定義了多個同名的HttpController類型,可能是多個HttpController類型在不區分大小寫情況下同名,或者是完全同名的多個HttpController類型定義在不同的命名空間下。

這兩種情況下自然不能通過GetControllerMapping方法返回的字典對象來判斷,但是卻可以通過用于緩存HttpController類型的HttpControllerTypeCache對象來判斷。對于第一種情況,DefaultHttpControllerSelector依然會拋出一個響應狀態為HttpStatusCode.NotFound的HttpResponseException異常。在第二種情況下,它會拋出一個InvalidOperationException異常,并提示“具有多個匹配的HttpController”。

四、HttpController的創建

通過上面的介紹我們知道利用注冊的HttpControllerSelector對象可以根據表示當前請求的HttpRequestMessage得到描述目標HttpController的HttpControllerDescriptor對象。在前面介紹HttpControllerDescriptor的時候我們提到過它自身就具有創建對應HttpController的能力。HttpControllerDescriptor創建被描述HttpController的能力體現在它的CreateController方法上。接下來我們就來著重介紹實現在這個CreateController方法中的HttpController創建機制。

   1: public class HttpControllerDescriptor
   2: {   
   3:     //其他成員
   4:     public virtual IHttpController CreateController(HttpRequestMessage request);
   5: }

1、HttpControllerActivator

針對請求對目標HttpController的激活機制最終落實到一個名為HttpControllerActivator的對象上,所有的HttpControllerActivator類型均實現了IHttpControllerActivator接口。如下面的代碼片斷所示,定義其中的唯一方法Create會根據表示當前請求的HttpRequestMessage對象、描述目標HttpController的HttpControllerDescriptor對象以及目標HttpController的類型來創建對應的HttpController對象。

   1: public interface IHttpControllerActivator
   2: {
   3:     IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType);
   4: }

我們已經知道了像這樣的“標準化組件”一定是注冊到當前ServicesContainer上被HttpController激活系統使用的。我們可以通過ServicesContainer具有如下定義的擴展方法GetHttpControllerActivator直接獲取注冊的HttpControllerActivator對象。

   1: public static class ServicesExtensions
   2: {
   3:     //其他成員
   4:     public static IHttpControllerActivator GetHttpControllerActivator(this ServicesContainer services);
   5: }

實際上HttpControllerDescriptor的CreateController方法就是調用這個擴展方法得到注冊的HttpControllerActivator對象,并調用它的Create方法來創建目標HttpController的。如下的代碼體現了CreateController方法真正的實現邏輯。

   1: public class HttpControllerDescriptor
   2: {
   3:     //其他成員
   4:     public virtual IHttpController CreateController(HttpRequestMessage request)
   5:     {        
   6:         return this.Configuration.Services.GetHttpControllerActivator().Create(request, this, this.ControllerType);
   7:     }
   8: }

2、DefaultHttpControllerActivator

我們照例利用通過DefaultServices的構造函數定義分析出默認注冊的HttpControllerActivator是個怎樣的對象。如下面的代碼片斷所示,當DefaultServices被初始化的時候它會創建并注冊一個類型為DefaultHttpControllerActivator對象。

   1: public class DefaultServices : ServicesContainer
   2: {
   3:     //其他成員
   4:     public DefaultServices(HttpConfiguration configuration)
   5:     {
   6:         //其他操作
   7:         this.SetSingle<IHttpControllerActivator>(new DefaultHttpControllerActivator());
   8:     }
   9: }

接下來我們就來分析一下在DefaultHttpControllerActivator類型的Create方法中是如何激活目標HttpController實例的,不過要真正了解實現在DefaultHttpControllerActivator的HttpController激活機制之前,我們需要認識另一個名為DependencyResolver的對象。

3、DependencyResolver

說到DependencyResolver,我們又不得不談到IoC的概念。我們知道IoC常和另一個術語“依賴注入(DI,Dependency Injection)”聯系在一起。通過IoC容器激活的對象可能具有針對其他對象的依賴,而且被依賴的對象可能具有針對另一個對象的依賴,所以IoC容器需要在提供所需對象之前幫助我們解決這些依賴。從命名也可以看出來,這里介紹DependencyResolver與依賴注入有關,我們可以將它視為ASP.NET Web API內部使用的IoC容器。所有的DependencyResolver實現了具有如下定義的接口IDependencyResolver,它定義在命名空間“System.Web.Http.Dependencies”下。這個接口的定義有點特別,它具有唯一個返回類型為IDependencyScope的BeginScope方法,IDependencyResolver接口本身同時也繼承IDependencyScope這個接口,并且這兩個接口又都繼承自IDisposable接口。

   1: public interface IDependencyResolver : IDependencyScope, IDisposable
   2: {
   3:     IDependencyScope BeginScope();
   4: }
   5:  
   6: public interface IDependencyScope : IDisposable
   7: {
   8:     object GetService(Type serviceType);
   9:     IEnumerable<object> GetServices(Type serviceType);
  10: }

通過DependencyResolver的BeginScope方法創建的IDependencyScope對象可以視為一個用于激活目標對象的上下文,我們可以通過調用它的GetService和GetServices方法根據指定的“服務接口類型”獲取對應的服務實例。由于IDependencyScope繼承自IDisposable,所以與此上下文關聯的資源釋放工作可以通過實現的Dispose方法來完成。

與上面我們介紹的那些“標準化組件”不同,默認使用的DependencyResolver并未注冊到當前的ServicesContainer對象上,而是直接注冊到了當前HttpConfiguration上面。如下面的代碼片斷所示,當前使用的DependencyResolver直接通過HttpConfiguration的DependencyResolver屬性來獲取和設置。

   1: public class HttpConfiguration : IDisposable
   2: {
   3:     //其他成員    
   4:     public HttpConfiguration(HttpRouteCollection routes)
   5:     {       
   6:         this._dependencyResolver = EmptyResolver.Instance;
   7: }   
   8:  
   9:     public IDependencyResolver DependencyResolver
  10:     {
  11:         get
  12:         {
  13:             return this._dependencyResolver;
  14:         }
  15:         set
  16:         {            
  17:             this._dependencyResolver = value;
  18:         }
  19:     }
  20: }

從上面的代碼片斷我們還可以看出默認注冊到HttpConfiguration上的DependencyResolver是通過類型EmptyResolver的靜態屬性Instance返回的EmptyResolver對象。EmptyResolver是一個定義在程序集“System.Web.Http.dll”中的內部類型,其成員定義如下。之所以將它如此命名,原因在于它僅僅是一個“空”的IoC容器。它的BeginScope返回的是它自身,GetService和GetServices方法分別返回Null和一個空對象集合,Dispose方法也沒有任何資源釋放工作要做。

   1: internal class EmptyResolver : IDependencyResolver, IDependencyScope, 
   2:     IDisposable
   3: {
   4:     public IDependencyScope BeginScope();
   5:     public void Dispose();
   6:     public object GetService(Type serviceType);
   7:     public IEnumerable<object> GetServices(Type serviceType);
   8:     public static IDependencyResolver Instance { get; }
   9: }

4、HttpRequestMessage中的DependencyResolver

雖然當前使用的DependencyResolver是注冊到當前HttpConfiguration上的,但是我們可以直接從表示當前請求的HttpRequestMessage對象中獲取由它創建的DependencyScope對象。如下面的代碼片斷所示,HttpRequestMessage具有一個返回類型為IDependencyScope接口的擴展方法GetDependencyScope。

   1: public static class HttpRequestMessageExtensions
   2: {
   3:     //其他成員    
   4:     public static IDependencyScope GetDependencyScope(this HttpRequestMessage request);
   5: }

其實這個擴展方法實現邏輯很簡單,因為DependencyScope對象也存放于HttpRequestMessage的屬性字典中。如果此DependencyScope對象尚未添加,該方法則會通過當前的HttpConfiguration得到注冊的DependencyResolver對象,然后利用它創建一個新的DependencyScope對象并添加到HttpRequestMessage對象的屬性字典中,后續過程如果需要使用到此DependencyScope就可以直接從HttpRequestMessage中提取了。

5、DependencyResolver在DefaultHttpControllerActivator中的應用

在對DependencyResolver有了基本了解后,我們再來討論DefaultHttpControllerActivator的Create方法是如何根據當前請求來激活目標HttpController對象的。其實實現機制非常簡單,DefaultHttpControllerActivator先通過調用表示當前請求的HttpRequestMessage對象的擴展方法GetDependencyScope得到通過當前DependencyResolver創建的DependencyScope對象,然后將目標HttpController的類型作為參數調用其GetService方法。如果該方法返回一個具體的HttpController對象,該對象就是Create方法的返回值,否則直接根據目標HttpController的類型進行反射創建一個HttpController對象并返回。如下所示的代碼片斷基本上體現了DefaultHttpControllerActivator的HttpController激活機制。

   1: public class DefaultHttpControllerActivator : IHttpControllerActivator
   2: {
   3:     public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
   4:     {
   5:         IDependencyScope depedencyScope = request.GetDependencyScope();
   6:         object httpController = depedencyScope.GetService(controllerType)?? Activator.CreateInstance(controllerType);
   7:         return httpController as IHttpController;
   8:     }
   9: }

由于默認請求下注冊到當前HttpConfiguration上的DependencyResolver是一個EmptyResolver對象,它的GetService方法總是返回Null,所以默認情況下對HttpController的激活總是利用針對目標HttpController類型的反射實現的。關于HttpController的激活,我還想強調一點,在默認情況下解析出來的所有HttpController類型會被緩存,創建的用于描述HttpController的HttpControllerDescriptor對象也會被緩存,但是HttpController激活系統并不會對創建的HttpController對象實施緩存。換言之,對于多個針對相同的HttpController類型的請求來說,最終被激活的HttpController實例都是不同的。


文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()