ASP.NET MVC Controller啟用系統詳解:預設實現

來源:互聯網
上載者:User

Controller啟用系統最終通過註冊的ControllerFactory建立相應的Conroller對象,如果沒有對ControllerFactory類型或者類型進行顯式註冊(通過調用當前ControllerBuilder的SetControllerFactory方法),預設使用的是一個DefaultControllerFactory對象,我們現在就來討論實現在DefaultControllerFactory類型中的預設Controller啟用機制。

一、Controller類型的解析

啟用目標Controller對象的前提是能夠正確解析出對應的Controller類型。對於DefaultControllerFactory來,用於解析目標Controller類型的資訊包括:通過與當前請求匹配的路由對象產生的RouteData(其中包含Controller的名稱和命名空間)和包含在當前ControllerBuilder中的命名空間。很對讀者可以首先想到的是通過Controller名稱得到對應的類型,並通過命名空間組成Controller類型的全名,最後遍曆所有程式集以此名稱去載入相應的類型即可。

這貌似一個不錯的解決方案,實際上則完全不可行。不要忘了作為請求地址URL一部分的Controller名稱是不區分大小寫,而類型名稱則是區分大小的;不論是註冊路由時指定的命名空間還是當前ControllerBuilder的預設命名空間,有可能是包含統配符(*)。由於我們不能通過給定的Controller名稱和命名空間得到Controller的真實類型名稱,自然就不可能通過名稱去解析Controller的類型了。

ASP.NET MVC的Controller啟用系統反其道而行之。它先遍曆通過BuildManager的靜態方法GetReferencedAssemblies方法得到的編譯Web應用所使用的程式集,通過反射得到所有實現了介面IController的類型,最後通過給定的Controller的名稱和命名空間作為匹配條件在這個預先擷取的類型列表中得到目標Controller的類型。

執行個體示範:建立一個自訂ControllerFactory類比Controller預設啟用機制

為了讓讀者對預設採用的Controller啟用機制,尤其是Controller類型的解析機制有一個深刻的認識,我們通過一個自訂的ControllerFactory來類比其中的實現。由於我們採用反射的方式來建立Controller對象,所以我們將該自訂ControllerFactory起名為ReflelctionControllerFactory。[原始碼從這裡下載]

   1: public class ReflelctionControllerFactory : IControllerFactory
2: {
3: //其他成員
4: private static List<Type> controllerTypes;
5: static ReflelctionControllerFactory()
6: {
7: controllerTypes = new List<Type>();
8: foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
9: {
10: controllerTypes.AddRange(assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)));
11: }
12: }
13:
14: public IController CreateController(RequestContext requestContext, string controllerName)
15: {
16: Type controllerType = this.GetControllerType(requestContext.RouteData, controllerName);
17: if (null == controllerType)
18: {
19: throw new HttpException(404, "No controller found");
20: }
21: return (IController)Activator.CreateInstance(controllerType);
22: }
23:
24: private static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace)
25: {
26: if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase))
27: {
28: return string.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase);
29: }
30: requestedNamespace = requestedNamespace.Substring(0, requestedNamespace.Length - ".*".Length);
31: if (!targetNamespace.StartsWith(requestedNamespace, StringComparison.OrdinalIgnoreCase))
32: {
33: return false;
34: }
35: return ((requestedNamespace.Length == targetNamespace.Length) || (targetNamespace[requestedNamespace.Length] == '.'));
36:     }
37:
38: private Type GetControllerType(IEnumerable<string> namespaces, Type[] controllerTypes)
39: {
40: var types = (from type in controllerTypes
41: where namespaces.Any(ns => IsNamespaceMatch(ns, type.Namespace))
42: select type).ToArray();
43: switch (types.Length)
44: {
45: case 0: return null;
46: case 1: return types[0];
47: default: throw new InvalidOperationException("Multiple types were found that match the requested controller name.");
48: }
49: }
50:
51: protected virtual Type GetControllerType(RouteData routeData, string controllerName)
52: {
53: //省略實現
54: }
55: }

如上面的代碼片斷所示,ReflelctionControllerFactory具有一個靜態controllerTypes欄位由於儲存所有Controller的類型。在靜態建構函式中,我們調用BuildManager的GetReferencedAssemblies方法得到所有用於編譯Web應用的程式集,並從中得到所有實現了IController介面的類型,這些類型全部被添加到通過靜態欄位controllerTypes表示的類型列表。

Controller類型的解析實現在受保護的GetControllerType方法中,在用於最終啟用Controller對象的CreateController方法中,我們通過調用該方法得到與指定RequestContext和Controller名稱相匹配的Controller類型,最終通過調用Activator的靜態方法CreateInstance根據該類型建立相應的Controller對象。如果不能找到匹配的Controller類型(GetControllerType方法返回Null),則拋出一個HTTP狀態為404的HttpException。

ReflelctionControllerFactory中定義了兩個輔助方法,IsNamespaceMatch用於判斷Controller類型真正的命名空間是否與指定的命名空間(可能包含統配符)相匹配,在進行字元比較過程中是忽略大小寫。私人方法GetControllerType根據指定的命名空間列表和類型名稱匹配的類型數組得到一個完全符合的Controller類型。如果得到多個匹配的類型,直接拋出InvalidOperation異常,並提示具有多個匹配的Controller類型;如果找不到匹配類型,則返回Null。

在如下所示的用於解析Controller類型的GetControllerType方法中,我們從預先得到的所有Controller類型列表中篩選出類型名稱與傳入的Controller名稱相匹配的類型。我們首先通過路由對象的命名空間對 之前 得到的類型列表進行進一步篩選,如果能夠找到一個唯一的類型,則直接將其作為Controller的類型返回。為了確定是否採用後備命名空間對Controller類型進行解析,我們從作為參數參數的RouteData對象的DataTokens中得到擷取一個Key為“UseNamespaceFallback”的元素,如果該元素存在並且值為False,則直接返回Null。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.