MvcHandler是一個mvc程序真正開始的地方,因為你可以直接看到并調試它的源碼。
MvcHandler的主要代碼如下:
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {AddVersionHeader(httpContext);// Get the controller typestring controllerName = RequestContext.RouteData.GetRequiredString("controller");// Instantiate the controller and call ExecuteIControllerFactory factory = ControllerBuilder.GetControllerFactory();IController controller = factory.CreateController(RequestContext, controllerName);if (controller == null) {throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName));}try {controller.Execute(RequestContext);}finally {factory.ReleaseController(controller);}}
這個方法的流程可以概括為: 找到Requst中的Controller name, 根據Controller name創建這個Controller, 執行這個Controller中執行被請求的
Action。
具體分析如下:
1. 添加Http Header
AddVersionHeader(httpContext);
添加一個Http Header: HTTP/1.1 200 OK?? …?? X-AspNetMvc-Version: 1.0…
2. 從路由表中找到請求的controller的名子
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
獲取路由表中的controller name, 在下面的代碼中根據這個controller name在緩存中查找到對應的controller類型并生成controller類。
3. 返回一個IControllerFactory對象
IControllerFactory factory = ControllerBuilder.GetControllerFactory();
返回一個繼承自IControllerFactory接口的類的實例,這里默認返回DefaultControllerFactory類。 ControllerBuilder屬性是ControllerBuilder類的一個靜態實例,在mvc程序第一次啟動時才會執行ControllerBuilder類的默認構造函數, 在這個構造函數將DefaultControllerFactory類的一個實例傳入到SetControllerFactory()方法中, 這樣做的目地是定義GetControllerFactory()的具體返回類型。ControllerBuilder類的構造函數代碼如下:
public ControllerBuilder() {SetControllerFactory(new DefaultControllerFactory() {ControllerBuilder = this});}所以想要改變GetControllerFactory()的默認返回類型的辦法就是在執行ControllerBuilder.GetControllerFactory()之前調用ControllerBuilder類中的
SetControllerFactory()方法,這個方法有兩個重載
public void SetControllerFactory(IControllerFactory controllerFactory) {if (controllerFactory == null) {throw new ArgumentNullException("controllerFactory");}_factoryThunk = () => controllerFactory;}public void SetControllerFactory(Type controllerFactoryType) {if (controllerFactoryType == null) {throw new ArgumentNullException("controllerFactoryType");}if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_MissingIControllerFactory,controllerFactoryType),"controllerFactoryType");}_factoryThunk = delegate() {try {return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);}catch (Exception ex) {throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,controllerFactoryType),ex);}};}只需要將我們自定義并繼承自IControllerFactory接口的類的一個實例或type傳入就可以。
4. 根據controller Name創建controller對象
IController controller = factory.CreateController(RequestContext, controllerName);
調用DefaultControllerFactory類中的CreateController方法創建controller類。 CreateController(…)方法的具體代碼如下:
public virtual IController CreateController(RequestContext requestContext, string controllerName){if (requestContext == null){throw new ArgumentNullException("requestContext");}if (String.IsNullOrEmpty(controllerName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");}RequestContext = requestContext;Type controllerType = GetControllerType(controllerName);IController controller = GetControllerInstance(controllerType);return controller;}這里比較簡單,首先執行GetControllerType(controllerName)找到對應的controll type, 再調用GetControllerInstance(controllerType) 反射出具體的controll類,先來看看GetControllerType(…)方法中的代碼:
protected internal virtual Type GetControllerType(string controllerName){if (String.IsNullOrEmpty(controllerName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");}// first search in the current route's namespace collectionobject routeNamespacesObj;Type match;if (RequestContext != null && RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)){IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;if (routeNamespaces != null){HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);match = GetControllerTypeWithinNamespaces(controllerName, nsHash);if (match != null){return match;}}}// then search in the application's default namespace collectionHashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);match = GetControllerTypeWithinNamespaces(controllerName, nsDefaults);if (match != null){return match;}// if all else fails, search every namespacereturn GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */);}RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)來返回一個namespace的集合, 一開始我對namespace很不理解,現在我明白了它的意思:在程序中不同的namespace下面可能會存在同名的controller,所以這里用namespace區分這些同名的但不同意義的controller。namespace可以在Global.asax.cs的RegisterRoutes(…)方法中指定,比如:
routes.MapRoute("Default", // Route name"{controller}/{action}/{id}", // URL with parametersnew { controller = "Home", action = "Index", id = "" }, // Parameter defaultsnew { httpMethod = new HttpMethodConstraint("get", "post") },new string[]{"Namespace1"});在繼續看GetControllerType(…)方法,在GetControllerType(string controllerName) 方法中最終都是通過調用GetControllerTypeWithinNamespaces(…)方法返回controller type的, 具體代碼如下:
private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces){// Once the master list of controllers has been created we can quickly index into itControllerTypeCache.EnsureInitialized(BuildManager);IList<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);switch (matchingTypes.Count){case 0:// no matching typesreturn null;case 1:// single matching typereturn matchingTypes[0];default:// multiple matching types// we need to generate an exception containing all the controller typesStringBuilder sb = new StringBuilder();foreach (Type matchedType in matchingTypes){sb.AppendLine();sb.Append(matchedType.FullName);}throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.DefaultControllerFactory_ControllerNameAmbiguous,controllerName, sb));}}首先將執行ControllerTypeCache.EnsureInitialized(BuildManager);? 他的作用是將程序中所有assembly中所有以Controller結尾的類放在緩存中,看一下
EnsureInitialized(…)方法的代碼:
ControllerBuilder.cs
public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List<Type> controllerTypes = GetAllControllerTypes(buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } }這是一個具有2級結構的緩存, 以controll name為key, 以Lookup<string, Type>對象為值保存到緩存中,而Lookup<string, Type>的結構是以namespace為key, 以
controller type為值的鍵值集合,這個2級結構的作用就是上面提到的用來解決不同namespace中同名controller的問題。
GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) 方法中:
ControllerTypeCache.GetControllerTypes(controllerName, namespaces) 就是去找具有相同controllerName的controller type,不過這里有個問題就是如果沒有在
Global中或其它地方提供默認的namespace而不同namespace下存在同名的controller,就導致GetControllerTypes(…)方法返回的controller數量大于1,這時程序會在
switch語句處拋出一個異常,所里一定要注意,盡量不要在不同的namespace中定義同名的controller
當找到一個對應的 controller type后,就將這個type返回給上面的CreateController(RequestContext requestContext, string controllerName) 方法中調用
GetControllerType(controllerName); 方法的地方, 然后再調用GetControllerInstance(controllerType); 方法將方法反射成具體的controller類并返回到ProcessRequest(…)中,并依次執行controller.Execute(RequestContext) –> factory.ReleaseController(controller);?? 至此整個MvcHandler的流程執行完畢.
附上MvcHandler的時序圖:
?
原文地址:深入分析 ASP.NET Mvc 1.0 – 1. 深入MvcHandler