前言
中秋歇了歇,途中也時不時去看看有關創建控制器的原理以及解析,時間拖得比較長,實在是有點心有余而力不足,但又想着既然諾下了要寫完原理一系列,還需有始有終。廢話少說,直入主題。
HttpControllerDispatcher
遺留問題 :在第六篇末尾所給圖中有一個HttpControllerDispatcher未進行敘述,在本篇中去敘述正合時宜。
在第六篇我們也說過在Web API消息處理管道中的最后一個處理程序是HttpRoutingDispacher,它被用來激活控制器,這樣說雖然沒錯,但是不夠具體,實際上是利用隸屬於它的HttpControllerDispatcher來激活控制器,而當我們調用HttpRoutingDispatcher中的SendAsync方法來處理請求,實際上是調用HttpControllerDispatcher中的一個私有方法 SendAsyncInternal 來處理請求,下面我們來看看這個方法的定義:
最重要的當屬以上這三個部分,下面我們一一來進行解析(放松心情,容我娓娓道來)。
通過上面我們看到了CreateController這個方法,是的,你沒看錯,就是這個方法里面生成了APIController,那是不是就講完了呢?當然不是,你得知道到底是怎樣創建的,在此方法中只是進行了調用了方法來創建APIController控制器而已,其中的細節還得我們慢慢去摸索。
第一步:HttpControllerDescriptor descriptor = this.ControllerSelector.SelectController(request)
我們首先看看這個屬性ControllerSelector的定義:
private IHttpControllerSelector ControllerSelector { get { if (this._controllerSelector == null) { this._controllerSelector = this._configuration.Services.GetHttpControllerSelector(); } return this._controllerSelector; } }
這個屬性是通過返回值為HttpConfiguration的屬性_configuration來獲取,接下來我們來看看HttpConfiguration的定義,列舉出下面最重要的兩個屬性:
public IDependencyResolver DependencyResolver { get; set; } public ServicesContainer Services { get; internal set; }
ServicesContainer是服務的容器,在消息處理管道中每一個環節都是通過注冊組件來完成相應的任務,而這些組件都是通過實現了某個接口而創建的,而服務容器正是這些接口的基點,ServicesContainer就是所謂的DI容器,將我們需要的服務注冊到其中,但是它只是預定義了許多接口,(IDependencyResolver作用也類似於ServicesContainer)所以我們需要看其子類 DefaultServices 的具體實現,我們來看看其子類構造函數傳入HttpConfiguration的比較重要的一個定義:
this.SetSingle<IHttpControllerSelector>(new DefaultHttpControllerSelector(configuration));
從此句知,上述的IHttpControllerSelector接口所定義的屬性ControllerSelector就是我們注冊的 DefaultHttpControllerSelector ,同時依上述也知,SelectController是通過此類而實現,所以我們再來看看此類中關於此方法的定義及實現:
1 public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request) 2 { 3 HttpControllerDescriptor descriptor; 4 string controllerName = this.GetControllerName(request); 5 if (this._controllerInfoCache.Value.TryGetValue(controllerName, out descriptor)) 6 { 7 return descriptor; 8 } 9 ICollection<Type> controllerTypes = this._controllerTypeCache.GetControllerTypes(controllerName); 10 }
我們首先看看是紅色標記第一個,它是如何獲得控制器名controllerName的,查看其GetControllerName方法,如下:
public virtual string GetControllerName(HttpRequestMessage request) { IHttpRouteData routeData = request.GetRouteData(); string str = null; routeData.Values.TryGetValue<string>("controller", out str); return str; }
我們知道當請求過來時會與注冊的路由進行匹配並解析並將其數據封裝到RouData對象中,以路由模板中大括號的值為鍵,以請求的URL中對應的控制器為值,所以上述就獲取RouteData中的鍵所對應的值並返回控制器名。
接下來我們再來看紅色標記第二個屬性_controllerInfoCache,此屬性存在於DefaultHttpControllerSelector,所以還是仔細看看該類定義:
1 public class DefaultHttpControllerSelector : IHttpControllerSelector 2 { 3 // Fields 4 private readonly HttpConfiguration _configuration; 5 private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _controllerInfoCache; 6 private readonly HttpControllerTypeCache _controllerTypeCache; 7 private const string ControllerKey = "controller"; 8 public static readonly string ControllerSuffix; 9 10 // Methods 11 static DefaultHttpControllerSelector(); 12 public DefaultHttpControllerSelector(HttpConfiguration configuration); 13 private static Exception CreateAmbiguousControllerException(IHttpRoute route, string controllerName, ICollection<Type> matchingTypes); 14 public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping(); 15 public virtual string GetControllerName(HttpRequestMessage request); 16 private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache(); 17 public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request); 18 }
根據_controllerInfoCache,我們從表面意思知是控制器信息緩存,這個得重點講述下,為何?因為當請求過來時,我們會選擇控制器,但是要選擇哪個控制器呢,因為對於控制器的類型解析如果使用反射將需要耗費大量的時間,肯定是慘不忍睹的,所以我們就對解析出來的控制類型進行緩存,這將大大提高效率並節約時間,所以由上知,控制器選擇器ControllerSelector的作用是兩個:
-
根據請求選擇相應的選擇控制器
-
生成控制器緩存
由上述知控制器的緩存類型,即屬性_controllerInfoCache的返回類型 Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> ,該字典中字符串為控制器名稱,而HttpControllerDescriptor是對控制器的相關描述類型,這個類型我們下面講,那它是怎么獲取到緩存的呢?這個時候我們就要看看上述紅色標記中關於DefaultHttpControllerSelector的構造函數,查看如下:
1 public DefaultHttpControllerSelector(HttpConfiguration configuration) 2 { 3 if (configuration == null) 4 { 5 throw Error.ArgumentNull("configuration"); 6 } 7 this._controllerInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(new Func<ConcurrentDictionary<string, HttpControllerDescriptor>>(this.InitializeControllerInfoCache)); 8 this._configuration = configuration; 9 this._controllerTypeCache = new HttpControllerTypeCache(this._configuration); 10 }
由上知,是通過使用延遲加載技術來對控制器緩存進行了創建而創建則是通過 InitializeControllerInfoCache 方法來進行,下面我們繼續看看該方法的實現:
1 private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache() 2 { 3 ConcurrentDictionary<string, HttpControllerDescriptor> dictionary = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); 4 HashSet<string> set = new HashSet<string>(); 5 foreach (KeyValuePair<string, ILookup<string, Type>> pair in this._controllerTypeCache.Cache) 6 { 7 string key = pair.Key; 8 foreach (IGrouping<string, Type> grouping in pair.Value) 9 { 10 foreach (Type type in grouping) 11 { 12 if (dictionary.Keys.Contains(key)) 13 { 14 set.Add(key); 15 break; 16 } 17 dictionary.TryAdd(key, new HttpControllerDescriptor(this._configuration, key, type)); 18 } 19 } 20 } 21 foreach (string str2 in set) 22 { 23 HttpControllerDescriptor descriptor; 24 dictionary.TryRemove(str2, out descriptor); 25 } 26 return dictionary; 27 }
注意上述該方法中的紅色標記知,最終是通過遍歷控制器類型緩存而來,所以我們的重點就是控制器類型緩存 HttpControllerTypeCache 。
那問題來了,該控制器緩存類是如何而來的呢?
這個時候就要用到服務容器ServiceContainer了即該子類DefaultServices注入的服務,如下:
this.SetSingle<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver());
再來看看此類的定義:
1 public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver 2 { 3 // Fields 4 private readonly Predicate<Type> _isControllerTypePredicate; 5 6 // Methods 7 public DefaultHttpControllerTypeResolver(); 8 public DefaultHttpControllerTypeResolver(Predicate<Type> predicate); 9 public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver); 10 internal static bool IsControllerType(Type t); 11 12 // Properties 13 protected Predicate<Type> IsControllerTypePredicate { get; } 14 }
通過上述方法根據過濾條件等來加載指定程序集中所有符合條件的控制其類型(ControllerTypes)。這個時候控制器類型緩存就急不可耐了,一旦加載了所有控制器類型,它就會通過構造函數獲得所有控制類型並進行分組。如下:
private Dictionary<string, ILookup<string, Type>> InitializeCache() { IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver(); return this._configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(assembliesResolver).GroupBy<Type, string>(t => t.Name.Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length), StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>(g => g.Key, g => g.ToLookup<Type, string>(t => (t.Namespace ?? string.Empty), StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); }
但是此時還不是最終的緩存類型,上述已經說過會通過 DefaultHttpControllerSelector 中的方法 InitializeControllerInfoCache 遍歷該控制器類型緩存所生成的類型為 Lazy<Dictionary<string, ILookup<string, Type>>> 而得到類型為 Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> 。此上就是整個生成控制器緩存的過程。
接下來就是進入最后一個處理程序 HttpControllerDispatcher 中的方法 SendAsyncInternal 方法的第二步,在選擇請求中的對應的控制器以及進行控制器緩存后並返回HttpControllerDescriptor來創建控制器以及激活控制器。請繼續往下看。
HttpControllerDescriptor
第二步:IHttpController controller = descriptor.CreateController(request)
我們看看此類的定義:
1 public class HttpControllerDescriptor 2 { 3 // Fields 4 private object[] _attrCached; 5 private HttpConfiguration _configuration; 6 private string _controllerName; 7 private Type _controllerType; 8 private readonly ConcurrentDictionary<object, object> _properties; 9 10 // Methods 11 public HttpControllerDescriptor(); 12 internal HttpControllerDescriptor(HttpConfiguration configuration); 13 public HttpControllerDescriptor(HttpConfiguration configuration, string controllerName, Type controllerType); 14 public virtual IHttpController CreateController(HttpRequestMessage request); 15 public virtual Collection<T> GetCustomAttributes<T>() where T: class; 16 public virtual Collection<IFilter> GetFilters(); 17 private void Initialize(); 18 internal void Initialize(HttpConfiguration configuration); 19 private static void InvokeAttributesOnControllerType(HttpControllerDescriptor controllerDescriptor, Type type); 20 21 // Properties 22 public HttpConfiguration Configuration { get; set; } 23 public string ControllerName { get; set; } 24 public Type ControllerType { get; set; } 25 public virtual ConcurrentDictionary<object, object> Properties { get; } 26 }
此類最重要的當屬上述紅色標記的CreateController方法了,我們查看其定義:
public virtual IHttpController CreateController(HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } return this.Configuration.Services.GetHttpControllerActivator().Create(request, this, this.ControllerType); }
接下來調用GetHttpControllerActivator方法:
public static IHttpControllerActivator GetHttpControllerActivator(this ServicesContainer services) { return services.GetServiceOrThrow<IHttpControllerActivator>(); }
而注冊實現IHttpControllerActivator接口,則依然是通過DefaultServices來實現,如下:
this.SetSingle<IHttpControllerActivator>(new DefaultHttpControllerActivator());
DefaultHttpControllerActivator負責激活控制器,那么是怎么樣激活控制器的呢?接下來看看DefaultHttpControllerActivator類的定義:
1 public class DefaultHttpControllerActivator : IHttpControllerActivator 2 { 3 // Fields 4 private object _cacheKey; 5 private Tuple<HttpControllerDescriptor, Func<IHttpController>> _fastCache; 6 7 // Methods 8 public DefaultHttpControllerActivator(); 9 public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType); 10 private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController> activator); 11 }
最重要的是上述GetInstanceOrActivator方法來生成並激活IHttpController,查看此方法的實現如下:
private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController> activator) { IHttpController service = (IHttpController) request.GetDependencyScope().GetService(controllerType); if (service != null) { activator = null; return service; } activator = TypeActivator.Create<IHttpController>(controllerType); return null; }
由上知,此時DefaultHttpControllerActivator會從HttpConfiguration中獲取DependencyResolver屬性對應的容器,若此時容器為空,並通過反射調用TypeActivator來生成並激活控制器。以上就是整個創建控制器的整個過程。接下來我們繼續來看文章開頭 SendAsyncInternal 方法的第三步,請繼續往下看!
HttpControllerContext
第三步:HttpControllerContext controllerContext = new HttpControllerContext(......)
由此句知,利用構造函數將返回值為IHttpController的Controller屬性以及返回值為HttpControllerDescriptor的屬性ControllerDescriptor進行賦值,而得到一個有關控制器的上下文,下面我們來看看此類的定義:
public class HttpControllerContext { // Fields private HttpConfiguration _configuration; private IHttpController _controller; private HttpControllerDescriptor _controllerDescriptor; private HttpRequestMessage _request; private IHttpRouteData _routeData; // Methods public HttpControllerContext(); public HttpControllerContext(HttpConfiguration configuration, IHttpRouteData routeData, HttpRequestMessage request); // Properties public HttpConfiguration Configuration { get; set; } public IHttpController Controller { get; set; } public HttpControllerDescriptor ControllerDescriptor { get; set; } public HttpRequestMessage Request { get; set; } public IHttpRouteData RouteData { get; set; } }
所謂的控制器上下文就是一個HttpControllerContext對象對應一個控制器的HttpContext,我們可以通過上述Controller屬性類設置這個HttpControllerContext對象,不僅如此,我們還可以通過上述ControllerDescriptor屬性來設置控制器的HttpControllerDescriptor對象。
我們通過上述創建了控制器上下文HttpControllerContext對象,並最終執行SendAsyncInternal方法的最后一句
return controller.ExecuteAsync(controllerContext, cancellationToken);
通過調用控制器上的ExecuteAsync方法將當前獲得的控制器上下文傳遞進去最終做出響應。(這就涉及到控制器的執行過程下一節講控制器的執行過程)
總結
(1)簡單回顧下控制器的創建的過程
由隸屬於HttpRoutingDispatcher類型的HttpControllerDispatcher中的一個返回值為IHttpControllerSelector的屬性ControllerSelector,這個ControllerSelector就是DefaultHttpControllerSelector,並調用DefaultHttpControllerSelector中的SelectController()方法,然后由此方法返回的HttpControllerDescriptor的類型變量descriptor,最終調用此變量中的CreateController來創建並激活控制器。
(2)關於控制器創建的詳細示意圖如下:(來源:控制器創建示意圖)