我们在上一篇文章中介绍Controller激活系统中所涉及到的一些类型,比如有关Controller类型的相关定义类型就包括了IController类型,IAsyncController类型,ControllerBase抽象类型和我们最终要使用的抽象类型Controller,这是ASP.NET MVC 框架中和Controller本身定义相关的类型。其他辅助类型,包括管理Controller的类型ControllerFactory,这个工厂负责Controller的生产和销毁。我们还涉及到另一个辅助类型,用于把系统默认定义的或者自定义的ControllerFactory注册到ASP.NET MVC框架中的类型ControllerBuilder。
Controller类型、ControllerFactory类型和ControllerBuilder类型,他们之间的关系可以描述为:ControllerBuilder是面向客户的,或者说是程序员和ASP.NET MVC框架之间的桥梁。我们通过ControllerBuilder类型的SetControllerFactory方法把我们自定义的ControllerFactory类型实例注册到ASP.NET MVC框架中,ControllerFactory类型用于管理Controller类型实例,其实也就是说ControllerFactory类型就是ASP.NET MVC框架中的一个扩展点。
我们今天主要讲Controller是怎么解析出来的,之所以把这一部分分开写,因为合在一起太长了,也说的不详细,如果大家对以上说的不太清楚,可以查看《白话ASP.NET MVC之二:Controller激活系统的概览》, 该文对ASP.NET MVC框架中所提到的Controlelr激活系统所涉及的类型有详细的介绍。
一、“路由系统”和“激活系统”是怎么关联起来的
上一篇文章有过讲述,我们在这里简单说一下。ASP.NET 的路由系统是建立在一个叫做UrlRoutingModule的HttpModule组件上的,针对请求的路由解析是通过注册HttpApplication对象的PostResolveRequestCache事件来实现的,为当前的请求动态映射到一个HttpHandler类型上,最终由该HttpHandler接管请求并处理。我们来看看UrlRoutingModule类型的代码吧。
1 [TypeForwardedFrom("System.Web.Routing,Version=3.5.0.0,Culture=Neutral,PublicKeyToken=31bf3856ad364e35")] 2 public class UrlRoutingModule : IHttpModule 3 { 4 private static readonly object _contextKey = new object(); 5 6 object _requestDataKey = 7 8 private RouteCollection _routeCollection; 9 10 public RouteCollection RouteCollection 11 { 12 get 13 { 14 if (this._routeCollection == null) 15 { 16 this._routeCollection = RouteTable.Routes; 17 } 18 return this._routeCollection; 19 } 20 set 21 22 value; 23 24 } 25 26 protected virtual void dispose() 27 28 29 30 Init(HttpApplication application) 31 32 if (application.Context.Items[UrlRoutingModule._contextKey] != 33 34 return; 35 36 application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey; 37 application.PostResolveRequestCache += new EventHandler(.OnApplicationPostResolveRequestCache); 38 39 40 void OnApplicationPostResolveRequestCache( sender,EventArgs e) 41 42 HttpContextBase context = new HttpContextwrapper(((HttpApplication)sender).Context); 43 .PostResolveRequestCache(context); 44 45 46 [Obsolete(This method is obsolete. Override the Init method to use the PostMapRequestHandler event.47 PostMapRequestHandler(HttpContextBase context) 48 49 50 51 PostResolveRequestCache(HttpContextBase context) 52 53 RouteData routeData = .RouteCollection.GetRouteData(context); 54 if (routeData == 55 56 57 58 IRouteHandler routeHandler = routeData.RouteHandler; 59 if (routeHandler == 60 61 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,SR.GetString(UrlRoutingModule_norouteHandler"),object[0])); 62 63 if (routeHandler is StopRoutingHandler) 64 65 66 67 RequestContext requestContext = RequestContext(context,routeData); 68 context.Request.RequestContext = requestContext; 69 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 70 if (httpHandler == 71 72 string.Format(CultureInfo.CurrentUICulture,1)">UrlRoutingModule_NoHttpHandler[] 73 74 routeHandler.GetType() 75 })); 76 77 if (!(httpHandler UrlAuthFailureHandler)) 78 79 context.RemapHandler(httpHandler); 80 81 82 if (FormsAuthenticationModule.FormsAuthrequired) 83 84 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current,1)">); 85 86 87 new HttpException(401,1)">Assess_Denied_Description3)); 88 89 90 IHttpModule.dispose() 91 92 .dispose(); 93 94 95 IHttpModule.Init(HttpApplication application) 96 97 .Init(application); 98 99 }
具体来说,该组件通过以RouteTable的静态属性Routes表示的路由表针对当前请求实施路由解析,如果有匹配,就会根据路由对象Route来生成RouteData路由数据对象,然后我们借助RouteData对象的RouteHandler属性获取想要的HttpHandler对象。在默认情况下这个RouteHandler属性所代表的对象是MvcRouteHandler。翠花,上代码:
1 /// <summary>Creates an object that implements the IHttpHandler interface and passes the request context to it.</summary> MvcRouteHandler : IRouteHandler IControllerFactory _controllerFactory; 6 Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class. 7 MvcRouteHandler() 8 9 10 11 class using the specified factory controller object.12 <param name="controllerFactory">The controller factory.</param> 13 MvcRouteHandler(IControllerFactory controllerFactory) 14 15 this._controllerFactory = controllerFactory; 16 17 18 Returns the HTTP handler by using the specified HTTP context.19 <returns>The HTTP handler.</returns> 20 <param name="requestContext">The request context.21 virtual IHttpHandler GetHttpHandler(RequestContext requestContext) 22 23 requestContext.HttpContext.SetSessionStateBehavior(.GetSessionStateBehavior(requestContext)); 24 MvcHandler(requestContext); 25 26 27 Returns the session behavior.28 The session behavior.29 SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) string text = (string)requestContext.RouteData.Values[controller]; 33 string.IsNullOrWhiteSpace(text)) 34 35 InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController); 36 37 IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); 38 controllerFactory.GetControllerSessionBehavior(requestContext,text); 39 40 41 Returns the HTTP handler by using the specified request context.42 43 IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) 45 46 .GetHttpHandler(requestContext); 47 48 }
在该类型里面包含了一个IControllerFactory类型成员字段,这个接口类型是所有ControllerFactory都要必须实现的接口,否则就不叫Controller的工厂了。MvcRouteHandler类型有两个构造函数,无参的没的说,另一个需要传递一个IControllerFactory类型的参数,这个参数用于初始化MvcRouteHandler类型内部包含的类型为IControllerFactory的_controllerFactory字段。当我们构造MvcRouteHandler实例的时候,如果我们调用了无参的构造函数,它会在内部使用ControllerBuilder.Current.GetControllerFactory()方法来获取我们通过ControllerBuilder类型注册的IControllerFactory类型的实例,代码很明显:
IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
MvcRouteHandler实现了IRouteHandler接口,目的只有一个,提供后续的HttpHandler,IRouteHandler接口定义如下:
interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); }
MvcRouteHandler会给我们直接返回MvcHandler对象,这个对象用于处理请求,包括激活Controler对象,代码最有说服力,这份代码,上篇文章也贴过,现在也贴一下把,上代码:
1 MvcHandler : IHttpAsyncHandler,IHttpHandler,IRequiresSessionState 2 3 struct ProcessRequestState 4 5 internal IAsyncController AsyncController; 6 7 IControllerFactory Factory; 8 9 RequestContext RequestContext; 10 11 internal ReleaseController() 12 13 this.Factory.ReleaseController(.AsyncController); 14 15 16 17 object _processRequestTag = 18 19 string MvcVersion = MvcHandler.GetMvcVersionString(); 20 21 Contains the header name of the ASP.NET MVC version. 22 string MvcVersionHeaderName = X-AspNetMvc-Version 23 24 ControllerBuilder _controllerBuilder; 25 26 ControllerBuilder ControllerBuilder 27 28 29 30 this._controllerBuilder == 31 { 32 this._controllerBuilder = ControllerBuilder.Current; 33 } 34 ._controllerBuilder; 35 36 37 38 39 40 41 42 Gets or sets a value that indicates whether the MVC response header is disabled. 43 true if the MVC response header is disabled; otherwise,false. 44 bool disableMvcResponseHeader 45 46 get 47 set 48 49 50 Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance. 51 true if the instance is reusable; otherwise,1)"> 52 IsReusable 53 54 55 56 false 57 58 59 60 Gets the request context. 61 62 RequestContext RequestContext 63 64 65 66 67 68 69 70 IHttpHandler.IsReusable 71 72 73 74 .IsReusable; 75 76 77 78 <see cref="T:System.Web.Mvc.MvcHandler" /> 79 80 <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception> 81 MvcHandler(RequestContext requestContext) 82 83 if (requestContext == 84 85 new ArgumentNullException(requestContext 86 87 this.RequestContext = 88 89 90 Adds the version header by using the specified HTTP context. 91 <param name="httpContext">The HTTP context. 92 AddVersionHeader(HttpContextBase httpContext) 93 94 if (!MvcHandler.disableMvcResponseHeader) 95 96 httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName,MvcHandler.MvcVersion); 97 98 99 100 Called by ASP.NET to begin asynchronous request processing.101 The status of the asynchronous call.102 103 <param name="callback">The asynchronous callback method.104 <param name="state">The state of the asynchronous object.105 virtual IAsyncResult BeginProcessRequest(HttpContext httpContext,AsyncCallback callback,1)"> state) 106 107 HttpContextBase httpContext2 = HttpContextwrapper(httpContext); 108 .BeginProcessRequest(httpContext2,callback,state); 109 110 111 Called by ASP.NET to begin asynchronous request processing using the base HTTP context.112 113 114 115 116 virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext,1)">117 118 IController controller; 119 IControllerFactory factory; 120 this.ProcessRequestinit(httpContext,1)">out controller,1)">out factory); 121 IAsyncController asyncController = controller as IAsyncController; 122 if (asyncController != 123 124 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback,1)"> asyncState,MvcHandler.ProcessRequestState innerState) 125 126 IAsyncResult result; 127 try 128 { 129 result = innerState.AsyncController.BeginExecute(innerState.RequestContext,asyncCallback,asyncState); 130 } 131 catch 132 133 innerState.ReleaseController(); 134 throw135 136 result; 137 }; 138 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult,1)">139 140 141 142 innerState.AsyncController.EndExecute(asyncResult); 143 144 finally 145 146 147 148 149 MvcHandler.ProcessRequestState invokeState = MvcHandler.ProcessRequestState 150 151 AsyncController = asyncController,152 Factory = factory,1)">153 RequestContext = .RequestContext 154 155 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext(); 156 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback,state,beginDelegate,endDelegate,invokeState,MvcHandler._processRequestTag,-1,synchronizationContext); 157 158 Action action = delegate 159 160 161 162 controller.Execute(.RequestContext); 163 164 165 166 factory.ReleaseController(controller); 167 168 }; 169 AsyncResultWrapper.BeginSynchronous(callback,action,MvcHandler._processRequestTag); 170 171 172 Called by ASP.NET when asynchronous request processing has ended.173 <param name="asyncResult">The asynchronous result.174 EndProcessRequest(IAsyncResult asyncResult) 175 176 AsyncResultWrapper.End(asyncResult,1)">177 178 179 GetMvcVersionString() 180 181 new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2182 183 184 Processes the request by using the specified HTTP request context.185 186 ProcessRequest(HttpContext httpContext) 187 188 HttpContextBase httpContext2 = 189 .ProcessRequest(httpContext2); 190 191 192 Processes the request by using the specified base HTTP request context.193 194 ProcessRequest(HttpContextBase httpContext) 195 196 197 IControllerFactory controllerFactory; 198 controllerFactory); 199 200 201 controller.Execute(202 203 204 205 controllerFactory.ReleaseController(controller); 206 207 208 209 void ProcessRequestinit(HttpContextBase httpContext,1)">out IController controller,1)"> IControllerFactory factory) 210 211 HttpContext current = HttpContext.Current; 212 if (current != null && ValidationUtility.IsValidationEnabled(current) == true213 214 ValidationUtility.EnableDynamicValidation(current); 215 216 .AddVersionHeader(httpContext); 217 .RemoveOptionalRoutingParameters(); 218 string requiredString = this.RequestContext.RouteData.GetrequiredString(219 factory = .ControllerBuilder.GetControllerFactory(); 220 controller = factory.CreateController(.RequestContext,requiredString); 221 if (controller == 222 223 224 225 factory.GetType(),1)">226 requiredString 227 })); 228 229 230 231 RemoveOptionalRoutingParameters() 232 233 RouteValueDictionary values = .RequestContext.RouteData.Values; 234 values.RemoveFromDictionary((keyvaluePair<string,1)">object> entry) => entry.Value == UrlParameter.Optional); 235 236 237 Enables processing of HTTP Web requests by a custom HTTP handler that implements the interface.238 An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example,Request,Response,Session,and Server) that are used to service HTTP requests.239 IHttpHandler.ProcessRequest(HttpContext httpContext) 240 241 .ProcessRequest(httpContext); 242 243 244 245 246 <param name="context">247 <param name="cb">248 <param name="exTradata">The data.249 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context,AsyncCallback cb,1)"> exTradata) 250 251 .BeginProcessRequest(context,cb,exTradata); 252 253 254 255 <param name="result">256 IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) 257 258 .EndProcessRequest(result); 259 260 }
MvcHandler类型的BeginProcessRequest方法用来处理请求,包括激活Controller实例等。由于MvcHandler类型同时实现了IHttpHandler接口和IHttpAsyncHandler接口,所以他总是异步方式去执行(调用BeginProcessRequest/EndProcessRequest方法)。BeginProcessRequest方法在执行的时候通过RequestContext对象的RouteData属性获得Controller的名字,然后在通过ControllerBuilder获得ControllerFactory对象,然后用Controller的名字和ControllerFactory对象来激活目标Controller实例。如果Controller类型实现了IAsyncController接口,那就以异步方式执行Controller,否则以同步方式执行。Controller对象成功执行后,MvcHandler对象会调用ControllerFactory对象ReleaseController方法来销毁Controller实例对象。
我们小结一下,ASP.NET MVC的路由系统和Controller的激活系统是通过这些对象关联起来的:请求Url ------->Route------->RouteData------->RouteHandler(MvcRouteHandler)-------->MvcRouteHandler------>MvcHandler,通过这些对象就能串起来了。
二、Controller的详细解析过程
我先来简述一下Controller解析的原理吧。Controller实例对象的解析是通过实现了IControllerFactory接口的ControllerFactory对象实现的,ControllerFactory是怎么来的呢?是通过调用ControllerBuilder的SetControllerFactory方法实现对ControllerFactory类型或者实例对象的注册。如果我们没有调用ControllerBuilder的SetControllerFactory方法对象ControllerFactory类型或者实例显示注册,系统会使用默认的ControllerFactory来完成对Controller对象的解析,这个对象就是DefaultControllerFactory类型,该类型的实现正好反映了ASP.NET MVC框架对Controller实例的激活采取的默认策略。今天我们就看看DefaultControllerFactory类型是如何把Controller对象激活的,这也是Controller激活系统的默认实现,我们可以扩展ControllerFactory类型,实现自定义的Controller激活策略。
我把代码流程写一下,在MvcHandler类型里面有两个方法,一个方法是:BeginProcessRequest(HttpContextBase httpContext,object state),该方法用于对请求进行处理;第二个方法是:ProcessRequestinit(HttpContextbase httpContext,out IController controller,out IControllerFactory controllerFactory),该方法就是定义了激活Controller算法的骨架,上代码吧,代码最无二意,我们先看BeginProcessRequest的代码:
1 2 3 4 5 6 7 10 this.ProcessRequestinit(httpContext,out controller,out factory); 11 IAsyncController asyncController = controller 12 14 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = 17 18 19 result =20 21 24 26 28 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = 29 30 32 34 37 39 MvcHandler.ProcessRequestState invokeState = 40 41 AsyncController =42 Factory =43 RequestContext = 45 SynchronizationContext synchronizationContext =46 48 Action action = 50 51 52 controller.Execute(53 54 56 58 59 60 }
在这个方法里面根据解析出来的Controller的类型来执行,如果是异步的Controller那就异步执行,否则就同步执行。在该方法里面,第三行,标红色的方法就是定义解析和执行Controller的算法骨架,就是我们要贴代码的第二个方法ProcessRequestinit,源码如下:
1 3 HttpContext current = 4 5 6 8 9 10 11 factory = 12 controller = factory.CreateController(this.RequestContext,1)">13 15 21 }
这个方法主要是获取ControllerFactory实例,根据获得的ControllerFactory对象激活Controller对象,红色标注的代码就是核心关键点。说明一点,这个方法只是定义了激活Controller算法的骨架,具体的实现在DefaultControllerFactory类型中。代码很简单,我相信大家看的清楚。
DefaultControllerFactory会先通过“MVC-ControllerTypeCache.xml”文件来加载Controller的类型,如果没有找到有效的数据才会通过调用BuildManager的静态方法GetReferencedAssemblies获取到系统所使用到的所有程序集,然后针对每个程序集通过反射的方式获得所有实现了IController接口的类型,并XML文件的形式缓存起来,缓存的文件名就是“MVC-ControllerTypeCache.xml”。然后我们把Controller的名称和命名空间作为匹配条件去查找对应的Controller类型。当我们获得了符合标准的真是的Controller类型后,DefaultControllerFactory对象通过反射的方式创建Controller类型的实例对象。解析逻辑不复杂,但是代码不少。首先我说明一点,把代码全部贴出来不是为了占篇幅,而是为了大家能看的更明白,我把完整的源码贴出来,大家可以仔细体会一下。
1 Represents the controller factory that is registered by default. 2 DefaultControllerFactory : IControllerFactory 3 4 DefaultControllerActivator : IControllerActivator 5 6 private Func<IDependencyResolver> _resolverThunk; 7 8 public DefaultControllerActivator() : this( 9 10 11 12 DefaultControllerActivator(IDependencyResolver resolver) 13 14 if (resolver == 16 this._resolverThunk = (() => DependencyResolver.Current); 17 18 19 resolver); 20 21 22 IController Create(RequestContext requestContext,Type controllerType) 23 24 IController result; 25 26 27 result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType)); 28 29 catch (Exception innerException) 30 31 32 controllerType 34 }),innerException); 36 38 39 40 readonly ConcurrentDictionary<Type,SessionStateBehavior> _sessionStateCache = new ConcurrentDictionary<Type,SessionStateBehavior> 42 static ControllerTypeCache _staticControllerTypeCache = ControllerTypeCache(); 43 IBuildManager _buildManager; 45 46 private IResolver<IControllerActivator> _activatorResolver; 47 48 IControllerActivator _controllerActivator; 50 51 ControllerTypeCache _instanceControllerTypeCache; 53 54 IControllerActivator ControllerActivator 56 58 this._controllerActivator != 59 60 ._controllerActivator; 61 62 this._controllerActivator = ._activatorResolver.Current; 63 64 65 66 67 IBuildManager BuildManager 68 69 70 71 this._buildManager == 72 73 this._buildManager = BuildManagerWrapper(); 74 75 ._buildManager; 77 78 79 this._buildManager = 80 81 82 83 85 87 this._controllerBuilder ?? 89 90 91 92 94 95 ControllerTypeCache ControllerTypeCache 97 99 this._instanceControllerTypeCache ?? DefaultControllerFactory._staticControllerTypeCache; 100 101 102 103 this._instanceControllerTypeCache =104 105 106 107 <see cref="T:System.Web.Mvc.DefaultControllerFactory" />108 public DefaultControllerFactory() : null,1)">110 111 class using a controller activator.<param name="controllerActivator">An object that implements the controller activator interface.114 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator,1)">115 116 117 118 internal DefaultControllerFactory(IControllerActivator controllerActivator,IResolver<IControllerActivator> activatorResolver,IDependencyResolver dependencyResolver) if (controllerActivator != 121 122 this._controllerActivator = controllerActivator; 123 124 125 IResolver<IControllerActivator> arg_44_1 = activatorResolver; 126 if (activatorResolver == 127 128 arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver),DefaultControllerFactory constructor129 130 this._activatorResolver = arg_44_1; 131 132 133 static InvalidOperationException CreateAmbiguousControllerException(RouteBase route,1)">string controllerName,ICollection<Type> matchingTypes) 134 135 StringBuilder stringBuilder = StringBuilder(); 136 foreach (Type current in138 stringBuilder.AppendLine(); stringBuilder.Append(current.FullName); 140 141 Route route2 = route Route; 142 message; 143 if (route2 != 144 145 message = controllerName,1)"> route2.Url,1)">149 stringBuilder,1)"> Environment.NewLine 151 }); 152 153 else 155 message = 156 158 160 162 InvalidOperationException(message); 164 165 static InvalidOperationException CreateDirectRouteAmbiguousControllerException(ICollection<Type>167 StringBuilder stringBuilder = 168 169 171 172 173 string message = 174 stringBuilder,1)"> Environment.NewLine }); 178 179 180 181 Creates the specified controller by using the specified request context.182 The controller.183 The context of the HTTP request,which includes the HTTP context and route data.<param name="controllerName">The name of the controller.186 <exception cref="T:System.ArgumentException"><paramref name="controllerName" /> parameter is null or empty.187 virtual IController CreateController(RequestContext requestContext,1)"> controllerName) 188 191 192 193 string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRoutematch()) 194 195 new ArgumentException(MvcResources.Common_NullOrEmpty,1)">controllerName197 Type controllerType = .GetControllerType(requestContext,controllerName); .GetControllerInstance(requestContext,controllerType); 199 200 201 Retrieves the controller instance for the specified request context and controller type.202 The controller instance.203 204 <param name="controllerType">The type of the controller.205 <exception cref="T:System.Web.HttpException"> 206 /// <paramref name="controllerType" /> is null.207 <exception cref="T:System.ArgumentException"> 208 cannot be assigned.209 <exception cref="T:system.invalidOperationException">An instance of cannot be created.210 IController GetControllerInstance(RequestContext requestContext,1)">211 if (controllerType == 214 404,MvcResources.DefaultControllerFactory_NoControllerFound,1)">216 requestContext.HttpContext.Request.Path 217 218 219 typeof(IController).IsAssignableFrom(controllerType)) 220 221 new ArgumentException(223 controllerType 224 }),1)">controllerType226 .ControllerActivator.Create(requestContext,1)">228 229 Returns the controller's session behavior.230 The controller's session behavior.231 232 233 SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext,1)">234 235 236 237 SessionStateBehavior.Default; 238 239 return DefaultControllerFactory._sessionStateCache.GetorAdd(controllerType,1)">(Type type) 241 SessionStateAttribute sessionStateAttribute = type.GetCustomAttributes(typeof(SessionStateAttribute),1)">true).OfType<SessionStateAttribute>().FirstOrDefault<SessionStateAttribute>242 if (sessionStateAttribute == 243 244 245 246 sessionStateAttribute.Behavior; 247 248 249 250 Retrieves the controller type for the specified name and request context.251 The controller type.252 253 254 virtual Type GetControllerType(RequestContext requestContext,1)">255 256 258 260 string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRoutematch())) 261 262 263 264 RouteData routeData = requestContext.RouteData; 265 if (routeData != null && routeData.HasDirectRoutematch()) 266 267 DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData); 268 269 obj; 270 if (routeData.DataTokens.TryGetValue(Namespaces",1)"> obj)) 271 272 IEnumerable<string> enumerable = obj as IEnumerable<string>273 if (enumerable != null && enumerable.Any<()) 274 275 HashSet<string> namespaces = new HashSet<(enumerable,StringComparer.OrdinalIgnoreCase); 276 Type controllerTypeWithinNamespaces = .GetControllerTypeWithinNamespaces(routeData.Route,controllerName,namespaces); 277 if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens[UseNamespaceFallback])) 278 279 controllerTypeWithinNamespaces; 280 281 282 283 this.ControllerBuilder.DefaultNamespaces.Count > 284 285 HashSet<string> namespaces2 = string>(.ControllerBuilder.DefaultNamespaces,1)">286 Type controllerTypeWithinNamespaces = 287 288 289 290 291 292 this.GetControllerTypeWithinNamespaces(routeData.Route,1)">293 294 295 static Type GetControllerTypeFromDirectRoute(RouteData routeData) 296 297 IEnumerable<RouteData> directRouteMatches = routeData.GetDirectRouteMatches(); 298 List<Type> list = new List<Type>299 foreach (RouteData current directRouteMatches) 300 301 302 303 Type targetControllerType = current.GetTargetControllerType(); 304 if (targetControllerType == 305 306 InvalidOperationException(MvcResources.DirectRoute_MissingControllerType); 307 308 list.Contains(targetControllerType)) 309 310 list.Add(targetControllerType); 311 312 313 314 if (list.Count == 315 316 317 318 319 320 return list[321 322 DefaultControllerFactory.CreateDirectRouteAmbiguousControllerException(list); 323 324 325 private Type GetControllerTypeWithinNamespaces(RouteBase route,HashSet< namespaces) 326 327 this.ControllerTypeCache.EnsureInitialized(.BuildManager); 328 ICollection<Type> controllerTypes = .ControllerTypeCache.GetControllerTypes(controllerName,1)">329 switch (controllerTypes.Count) 330 331 case : 332 333 334 return controllerTypes.First<Type>335 default336 DefaultControllerFactory.CreateAmbiguousControllerException(route,controllerTypes); 337 338 339 340 Releases the specified controller.341 <param name="controller">The controller to release.342 ReleaseController(IController controller) 343 344 Idisposable disposable = controller Idisposable; 345 if (disposable != 346 347 disposable.dispose(); 348 349 350 351 internal IReadOnlyList<Type> GetControllerTypes() 352 353 354 .ControllerTypeCache.GetControllerTypes(); 355 356 357 This API supports the ASP.NET MVC infrastructure and is not intended to be used directly from your code. This method calls the <see cref="M:System.Web.Mvc.DefaultControllerFactory.GetControllerSessionBehavior(System.Web.Routing.RequestContext,System.Type)" /> method.358 359 360 The controller name.361 SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext,1)">362 363 364 365 366 367 .IsNullOrEmpty(controllerName)) 368 369 370 371 Type controllerType = 372 .GetControllerSessionBehavior(requestContext,1)">373 374 }
既然是ControllerFactory,DefaultControllerFactory肯定也实现了IControllerFactory接口,别的我们就不看了,我们看看是如何创建Controller对象的,方法代码如下:
2 3 4 5 6 9 10 11 12 17 Type controllerType = this18 return 19 }
代码很简单,该方法获取Controller的Type类型对象,然后根据Type对象创建实例。这个方法里面有两个辅助方法,一个是GetControllerType方法,另一个是GetControllerInstance方法,根据名称我们就能知道是做什么的。我们先看看GetControllerType方法的源码吧,这里是关键,没有Type对象的获取,以后都是空言:
9 11 13 15 RouteData routeData =16 18 20 23 IEnumerable<26 HashSet<27 Type controllerTypeWithinNamespaces = 28 30 34 36 HashSet<37 Type controllerTypeWithinNamespaces = 40 42 43 44 }
我们先根据RouteData路由数据来获取Controller的类型对象,如果RouteData不为空,并且在RouteData的Values属性中包含Key为“MS_DirectRouteMatches”的值,那我们就据此获取Controller的类型对象,如果没找到就返回Null值,如果有一个值,就会作为Controller的Type类型值返回,如果多于一个就会抛出异常。如果RouteData不包含Key为“MS_DirectRouteMatches”的值,我们就根据RouteData对象中DataTokens属性Key为“Namespaces”来获取Controller的Type对象,同理,如果没找到就返回null,找到一个就直接返回,如果多余一个的话就抛出异常。
如果我们还是没找到怎么办呢?我们就要看看能不能使用后备命名空间,如果可以,就根据此命名空间来查找,我们从RouteData路由数据的DataTokens属性中查找是否包含有Key为“UseNamespaceFallback”的值,如果有,并且是False,就直接返回,结束查找,如果不包含Key为“UseNamepsaceFallback”的值或者该值为True,我们就可以根据ControllerBuilder的DefaultNamespaces属性表示后备命名空间查找Controller的类型,同理,没有找到就返回null,找到一个就作为结果值直接返回,如果多于一个那就要抛出异常了。
好了,大概的逻辑写完了,如果还有不明白的,就仔细的看看源码把,如果把源码完整的看一遍,然后在整理逻辑,就容易多了。看代码别太注意代码细节,关注主要的关键点或者叫关键类就可以,把哥哥类之间的关系和责任整理清楚,理解起来并不复杂。
三、扩展点
到了现在,Controller激活系统就写的差不多了,唯一还差一点的就是扩展点还没提。ASP.NET MVC号称几乎任何地方都可以扩展,Controller激活系统中肯定也包含着扩展点,下来我们一一详述。
扩展点一:扩展IControllerFactory接口
第一个很容易想到的扩展点就是实现IControllerFactory接口,定义一个全新的ControllerFactory类型。如果是这样,里面所有的功能的就需要全部重新实现,包括Controller类型的解析、Controller实例的创建和销毁、也包括会话状态行为模式的管理。这样的好处就是我们就有更好的控制权了,可以按着我们的思想实现其功能。但是如果觉得重新实现有点麻烦,而且有时候也没有必要,拿我们就可以继承DefaultControllerFactory类型,针对特定的方法去扩展,实现我们自己的功能要求。比如我们想增加Ioc,有了IOC可以解耦Controller和Model的关系。一般来说,Controller实例的创建才需要IOC容器的控制,我们直接从DefaultControllerFactory类型继承,重写Controller实例创建的逻辑即可,这样也避免了全部重新实现,做起事来就更事半功倍了。
DefaultControllerFactory类型在创建Controller实例的时候,有两个方法是可以扩展的,一个是受保护的虚方法GetControllerType,我们可以定义自己的Controller类型的解析方法的实现,方法签名如下:
protected string controllerName)
其实这也是一个扩展点,但是我们今天不扩展这里,因为IOC框架和这里的关系不大,我们要扩展的是第二受保护的虚方法,方法签名如下:
virtual IController GetControllerInstance(RequestContext requestContext,Type controllerType)
这个方法是用于创建Controller实例的,这里正好可以使用IOC容器来管理Controller实例,当然在DefaultControllerFactory类型中还有其他的扩展点,大家可以自己去看,全部代码我以前也贴出来过。
1 UnityControllerFactory:DefaultControllerFactory <summary> /// IOC的容器对象 public IUnityContainer UnityContainer { get; ; } 8 9 通过构造函数初始化IOC的容器对象 10 11 <param name="unityContainer">传入的IOC容器对象 UnityControllerFactory(IUnityContainer unityContainer) if (unityContainer != this.UnityContainer = unityContainer; 20 容器对象不能为空!23 24 25 /// 26 27 <param name="requestContext"></param> 28 <param name="controllerType"></param> 29 <returns></returns> 30 override32 34 36 return (IController).UnityContainer.Resolve(controllerType); 38 }
代码很简单,我们将解析出来的Controller类型作为参数调用UnityContainer对象的Resolve方法,进而得到最终激活的Controller对象。这是样例代码,具体项目中,还要引入Unity的DLL,还需要注册,这些我就不细说了。
扩展点二:扩展ControllerActivator
DefaultControllerFactory对象并不直接激活目标Controller对象,真正激活Controller对象的是一个叫做ControllerActiviator类型的对象,换句话说,DefaultControllerFactory对象委托ControllerActivator对象根据解析出来类型创建对应Controller对象。所有的ControllerActivator对象均实现IControllerActivator接口,该接口定义如下:
Provides fine-grained control over how controllers are instantiated using dependency injection. IControllerActivator { When implemented in a class,creates a controller.</summary> The created controller.</returns> </param> IController Create(RequestContext requestContext,Type controllerType); }
该接口中定义了Create方法实现针对目标Controller对象的创建,两个参数分别表示请求上下文和Controller的类型。我们在看看DefaultControllerFactory和ControllerActivator之间的关系吧,DefaultControllerFactory类型有三个构造函数,代码如下:
4 } 7 11 14 16 17 19 IResolver<IControllerActivator> arg_44_1 =22 arg_44_1 = new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver),1)">24 25 }
第二个和第三个构造函数都有一个IControllerActivator类型的参数,第三个是程序集内部的,暂时不说,就说第二吧,DefaultControllerFactory正式通过该参数来创建目标Controller对象的,我们基于ControllerActivator对象来创建一个DefaultControllerFactory对象,他会最终被用于创建目标的Controller对象,我们的扩展点也就找到了。
我们扩展的源码如下:
1 NinjectControllerActivator : IControllerActivator 3 public IKernel Kernel { 4 5 NinjectControllerActivator() 7 this.Kernel = StandardKernel(); 11 .Kernel.TryGet(controllerType); 13 14 void Register<TFrom,TTo>() where TTo : TFrom 16 this.Kernel.Bind<TFrom>().To<TTo>18 }
扩展写好了,还需要在Global.asax文件里面进行注册,否则不能使用。IOC相关的内容就不介绍了,大家可以自行研究。
扩展点三:扩展DependencyResolver
如果我们在构建DefaultControllerFactory对象的时候没有显示指定采用的ControllerActiviator参数,则它会使用默认的一个ControllerActivator对象DefaultControllerActivator来完成,该类型是一个私有类型,外界无法访问。源码如下:
1 3 5 8 18 19 IController result; 22 24 result = (IController)(26 30 controllerType }),1)">35 }
其实DefaultControllerActivator对象要像创建目标Controller对象,还依赖另外一个叫DependencyResolver的对象。所有的DenpendencyResolver类型均实现了IDenpendencyResolver接口,接口定义如下:
Defines the methods that simplify service location and dependency resolution. IDependencyResolver 4 Resolves singly registered services that support arbitrary object creation. 5 The requested service or object.<param name="serviceType">The type of the requested service or object. GetService(Type serviceType); 9 Resolves multiply registered services.10 The requested services.The type of the requested services.12 IEnumerable<object> GetServices(Type serviceType); 13 }
系统中使用的DependencyResolver是通过DependencyResolver类型注册的。DependencyResolver类型有一个叫做Current的静态属性,该属性表示的是当前的、IDependencyResolver类型的对象。我们可以通过静态方法SetResolver来注册IDependencyResolver对象。需要说明一点,还有静态方法的DependencyResolver类型并没有实现IDependencyResolver接口,所以说该类型并不是真正的DependencyResolver类型,我们可以把它理解为一个Facade,它汇集了所要使用的对象和方法而已,使用更方便。
如果我们没有对DependencyResolver进行显示的注册,则系统会使用一个默认的叫做DefaultDependencyResolver的对象,本对象也是一个私有类型,外界无法使用。
当我们通过无参的构造函数或者通过将ControllerActivator参数指定为null的构造函数来创建DefaultControllerFactory实例的时候,那么用于激活Controller实例的将是通过DependencyResolver类型的静态属性Current表示的DependencyResolver对象来激活的,我们的扩展终于也找到了。
最后实现的代码如下:
NinjectDependencyResolver : IDependencyResolver NinjectDependencyResolver() 10 14 GetService(Type serviceType) 17 .Kernel.TryGet(serviceType); 19 public IEnumerable< GetServices(Type serviceType) .Kernel.GetAll(serviceType); 24 }
好了,扩展点写完 ,大家有时间可以看看源码,可以有很多启发,获得的也更多。
四、小结
我们可以小结了,到此,Controler激活系统这个小节我就写完了。其实不是很复杂,大家在看的时候,刚开始别太关注代码的细节,先把握整个数据流向,或者叫请求脉络,把里面所涉及到的对象和关系都整理清楚了,然后再有针对性的去看涉及到的每个对象,不是画画图,不是画画,是画各个对象之间的关系图,图能画出来,说明你的心中就有了整体把握了。如果太关注细节,太关注某个类型的代码,也会很容易顾此失彼,因为我开始就是那样,总是看了后面忘记前面,请求的线路也不是很清楚。所以我们学习的时候也要有方法,方法对,学的就快,成就感就能很快产生。好了,说了这么多了,继续努力吧,希望我写的东西对大家有所帮助。