白话ASP.NET MVC之三:Controller是如何解析出来的
来源:互联网 发布:硬笔字帖软件 编辑:程序博客网 时间:2024/06/07 09:31
我们在上一篇文章中介绍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 public class UrlRoutingModule : IHttpModule 2 { 3 private static readonly object _contextKey = new object(); 4 5 private static readonly object _requestDataKey = new object(); 6 7 private RouteCollection _routeCollection; 8 9 public RouteCollection RouteCollection10 {11 get12 {13 if (this._routeCollection == null)14 {15 this._routeCollection = RouteTable.Routes;16 }17 return this._routeCollection;18 }19 set20 {21 this._routeCollection = value;22 }23 }24 25 protected virtual void Dispose()26 {27 }28 29 protected virtual void Init(HttpApplication application)30 {31 if (application.Context.Items[UrlRoutingModule._contextKey] != null)32 {33 return;34 }35 application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;36 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);37 }38 39 private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)40 {41 HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);42 this.PostResolveRequestCache(context);43 }44 45 [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]46 public virtual void PostMapRequestHandler(HttpContextBase context)47 {48 }49 50 public virtual void PostResolveRequestCache(HttpContextBase context)51 {52 RouteData routeData = this.RouteCollection.GetRouteData(context);53 if (routeData == null)54 {55 return;56 }57 IRouteHandler routeHandler = routeData.RouteHandler;58 if (routeHandler == null)59 {60 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));61 }62 if (routeHandler is StopRoutingHandler)63 {64 return;65 }66 RequestContext requestContext = new RequestContext(context, routeData);67 context.Request.RequestContext = requestContext;68 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);69 if (httpHandler == null)70 {71 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]72 {73 routeHandler.GetType()74 }));75 }76 if (!(httpHandler is UrlAuthFailureHandler))77 {78 context.RemapHandler(httpHandler);79 return;80 }81 if (FormsAuthenticationModule.FormsAuthRequired)82 {83 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);84 return;85 }86 throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));87 }88 89 void IHttpModule.Dispose()90 {91 this.Dispose();92 }93 94 void IHttpModule.Init(HttpApplication application)95 {96 this.Init(application);97 }98 }
具体来说,该组件通过以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> 2 public class MvcRouteHandler : IRouteHandler 3 { 4 private IControllerFactory _controllerFactory; 5 6 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class.</summary> 7 public MvcRouteHandler() 8 { 9 }10 11 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class using the specified factory controller object.</summary>12 /// <param name="controllerFactory">The controller factory.</param>13 public MvcRouteHandler(IControllerFactory controllerFactory)14 {15 this._controllerFactory = controllerFactory;16 }17 18 /// <summary>Returns the HTTP handler by using the specified HTTP context.</summary>19 /// <returns>The HTTP handler.</returns>20 /// <param name="requestContext">The request context.</param>21 protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)22 {23 requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));24 return new MvcHandler(requestContext);25 }26 27 /// <summary>Returns the session behavior.</summary>28 /// <returns>The session behavior.</returns>29 /// <param name="requestContext">The request context.</param>30 protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)31 {32 string text = (string)requestContext.RouteData.Values["controller"];33 if (string.IsNullOrWhiteSpace(text))34 {35 throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);36 }37 IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();38 return controllerFactory.GetControllerSessionBehavior(requestContext, text);39 }40 41 /// <summary>Returns the HTTP handler by using the specified request context.</summary>42 /// <returns>The HTTP handler.</returns>43 /// <param name="requestContext">The request context.</param>44 IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)45 {46 return this.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接口定义如下:
1 public interface IRouteHandler2 {3 IHttpHandler GetHttpHandler(RequestContext requestContext);4 }
MvcRouteHandler会给我们直接返回MvcHandler对象,这个对象用于处理请求,包括激活Controler对象,代码最有说服力,这份代码,上篇文章也贴过,现在也贴一下把,上代码:
1 /// <summary>Selects the controller that will handle an HTTP request.</summary> 2 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState 3 { 4 private struct ProcessRequestState 5 { 6 internal IAsyncController AsyncController; 7 8 internal IControllerFactory Factory; 9 10 internal RequestContext RequestContext; 11 12 internal void ReleaseController() 13 { 14 this.Factory.ReleaseController(this.AsyncController); 15 } 16 } 17 18 private static readonly object _processRequestTag = new object(); 19 20 internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString(); 21 22 /// <summary>Contains the header name of the ASP.NET MVC version.</summary> 23 public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version"; 24 25 private ControllerBuilder _controllerBuilder; 26 27 internal ControllerBuilder ControllerBuilder 28 { 29 get 30 { 31 if (this._controllerBuilder == null) 32 { 33 this._controllerBuilder = ControllerBuilder.Current; 34 } 35 return this._controllerBuilder; 36 } 37 set 38 { 39 this._controllerBuilder = value; 40 } 41 } 42 43 /// <summary>Gets or sets a value that indicates whether the MVC response header is disabled.</summary> 44 /// <returns>true if the MVC response header is disabled; otherwise, false.</returns> 45 public static bool DisableMvcResponseHeader 46 { 47 get; 48 set; 49 } 50 51 /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary> 52 /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns> 53 protected virtual bool IsReusable 54 { 55 get 56 { 57 return false; 58 } 59 } 60 61 /// <summary>Gets the request context.</summary> 62 /// <returns>The request context.</returns> 63 public RequestContext RequestContext 64 { 65 get; 66 private set; 67 } 68 69 /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary> 70 /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns> 71 bool IHttpHandler.IsReusable 72 { 73 get 74 { 75 return this.IsReusable; 76 } 77 } 78 79 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcHandler" /> class.</summary> 80 /// <param name="requestContext">The request context.</param> 81 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception> 82 public MvcHandler(RequestContext requestContext) 83 { 84 if (requestContext == null) 85 { 86 throw new ArgumentNullException("requestContext"); 87 } 88 this.RequestContext = requestContext; 89 } 90 91 /// <summary>Adds the version header by using the specified HTTP context.</summary> 92 /// <param name="httpContext">The HTTP context.</param> 93 protected internal virtual void AddVersionHeader(HttpContextBase httpContext) 94 { 95 if (!MvcHandler.DisableMvcResponseHeader) 96 { 97 httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName, MvcHandler.MvcVersion); 98 } 99 }100 101 /// <summary>Called by ASP.NET to begin asynchronous request processing.</summary>102 /// <returns>The status of the asynchronous call.</returns>103 /// <param name="httpContext">The HTTP context.</param>104 /// <param name="callback">The asynchronous callback method.</param>105 /// <param name="state">The state of the asynchronous object.</param>106 protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)107 {108 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);109 return this.BeginProcessRequest(httpContext2, callback, state);110 }111 112 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>113 /// <returns>The status of the asynchronous call.</returns>114 /// <param name="httpContext">The HTTP context.</param>115 /// <param name="callback">The asynchronous callback method.</param>116 /// <param name="state">The state of the asynchronous object.</param>117 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)118 {119 IController controller;120 IControllerFactory factory;121 this.ProcessRequestInit(httpContext, out controller, out factory);122 IAsyncController asyncController = controller as IAsyncController;123 if (asyncController != null)124 {125 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)126 {127 IAsyncResult result;128 try129 {130 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);131 }132 catch133 {134 innerState.ReleaseController();135 throw;136 }137 return result;138 };139 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)140 {141 try142 {143 innerState.AsyncController.EndExecute(asyncResult);144 }145 finally146 {147 innerState.ReleaseController();148 }149 ;150 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState151 {152 AsyncController = asyncController,153 Factory = factory,154 RequestContext = this.RequestContext155 };156 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();157 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);158 }159 Action action = delegate160 {161 try162 {163 controller.Execute(this.RequestContext);164 }165 finally166 {167 factory.ReleaseController(controller);168 }169 };170 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);171 }172 173 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>174 /// <param name="asyncResult">The asynchronous result.</param>175 protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)176 {177 AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag);178 }179 180 private static string GetMvcVersionString()181 {182 return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);183 }184 185 /// <summary>Processes the request by using the specified HTTP request context.</summary>186 /// <param name="httpContext">The HTTP context.</param>187 protected virtual void ProcessRequest(HttpContext httpContext)188 {189 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);190 this.ProcessRequest(httpContext2);191 }192 193 /// <summary>Processes the request by using the specified base HTTP request context.</summary>194 /// <param name="httpContext">The HTTP context.</param>195 protected internal virtual void ProcessRequest(HttpContextBase httpContext)196 {197 IController controller;198 IControllerFactory controllerFactory;199 this.ProcessRequestInit(httpContext, out controller, out controllerFactory);200 try201 {202 controller.Execute(this.RequestContext);203 }204 finally205 {206 controllerFactory.ReleaseController(controller);207 }208 }209 210 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)211 {212 HttpContext current = HttpContext.Current;213 if (current != null && ValidationUtility.IsValidationEnabled(current) == true)214 { ValidationUtility.EnableDynamicValidation(current);215 } this.AddVersionHeader(httpContext);216 this.RemoveOptionalRoutingParameters();217 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");218 factory = this.ControllerBuilder.GetControllerFactory();219 controller = factory.CreateController(this.RequestContext, requiredString);220 if (controller == null)221 {222 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]223 {224 factory.GetType(),225 requiredString226 }));227 }228 }229 230 private void RemoveOptionalRoutingParameters()231 {232 RouteValueDictionary values = this.RequestContext.RouteData.Values;233 values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional);234 }235 236 /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary>237 /// <param name="httpContext">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.</param>238 void IHttpHandler.ProcessRequest(HttpContext httpContext)239 {240 this.ProcessRequest(httpContext);241 }242 243 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>244 /// <returns>The status of the asynchronous call.</returns>245 /// <param name="context">The HTTP context.</param>246 /// <param name="cb">The asynchronous callback method.</param>247 /// <param name="extraData">The data.</param>248 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)249 {250 return this.BeginProcessRequest(context, cb, extraData);251 }252 253 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>254 /// <param name="result">The asynchronous result.</param>255 void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)256 {257 this.EndProcessRequest(result);258 }259 }
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,AsyncCallback callback,object state),该方法用于对请求进行处理;第二个方法是:ProcessRequestInit(HttpContextbase httpContext,out IController controller,out IControllerFactory controllerFactory),该方法就是定义了激活Controller算法的骨架,上代码吧,代码最无二意,我们先看BeginProcessRequest的代码:
1 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 2 { 3 IController controller; 4 IControllerFactory factory; 5 this.ProcessRequestInit(httpContext, out controller, out factory); 6 IAsyncController asyncController = controller as IAsyncController; 7 if (asyncController != null) 8 { 9 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)10 {11 IAsyncResult result;12 try13 {14 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);15 }16 catch17 {18 innerState.ReleaseController();19 throw;20 }21 return result;22 };23 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)24 {25 try26 {27 innerState.AsyncController.EndExecute(asyncResult);28 }29 finally30 {31 innerState.ReleaseController();32 }33 };34 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState35 {36 AsyncController = asyncController,37 Factory = factory,38 RequestContext = this.RequestContext39 };40 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();41 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);42 }43 Action action = delegate44 {45 try46 {47 controller.Execute(this.RequestContext);48 }49 finally50 {51 factory.ReleaseController(controller);52 }53 };54 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);55 }
在这个方法里面根据解析出来的Controller的类型来执行,如果是异步的Controller那就异步执行,否则就同步执行。在该方法里面,第三行,标红色的方法就是定义解析和执行Controller的算法骨架,就是我们要贴代码的第二个方法ProcessRequestInit,源码如下:
1 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) 2 { 3 HttpContext current = HttpContext.Current; 4 if (current != null && ValidationUtility.IsValidationEnabled(current) == true) 5 { 6 ValidationUtility.EnableDynamicValidation(current); 7 } 8 this.AddVersionHeader(httpContext); 9 this.RemoveOptionalRoutingParameters();10 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");11 factory = this.ControllerBuilder.GetControllerFactory();12 controller = factory.CreateController(this.RequestContext, requiredString);13 if (controller == null)14 {15 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]16 {17 factory.GetType(),18 requiredString19 }));20 }21 }
这个方法主要是获取ControllerFactory实例,根据获得的ControllerFactory对象激活Controller对象,红色标注的代码就是核心关键点。说明一点,这个方法只是定义了激活Controller算法的骨架,具体的实现在DefaultControllerFactory类型中。代码很简单,我相信大家看的清楚。
DefaultControllerFactory会先通过“MVC-ControllerTypeCache.xml”文件来加载Controller的类型,如果没有找到有效的数据才会通过调用BuildManager的静态方法GetReferencedAssemblies获取到系统所使用到的所有程序集,然后针对每个程序集通过反射的方式获得所有实现了IController接口的类型,并XML文件的形式缓存起来,缓存的文件名就是“MVC-ControllerTypeCache.xml”。然后我们把Controller的名称和命名空间作为匹配条件去查找对应的Controller类型。当我们获得了符合标准的真是的Controller类型后,DefaultControllerFactory对象通过反射的方式创建Controller类型的实例对象。解析逻辑不复杂,但是代码不少。首先我说明一点,把代码全部贴出来不是为了占篇幅,而是为了大家能看的更明白,我把完整的源码贴出来,大家可以仔细体会一下。
1 /// <summary>Represents the controller factory that is registered by default.</summary> 2 public class DefaultControllerFactory : IControllerFactory 3 { 4 private class DefaultControllerActivator : IControllerActivator 5 { 6 private Func<IDependencyResolver> _resolverThunk; 7 8 public DefaultControllerActivator() : this(null) 9 { 10 } 11 12 public DefaultControllerActivator(IDependencyResolver resolver) 13 { 14 if (resolver == null) 15 { 16 this._resolverThunk = (() => DependencyResolver.Current); 17 return; 18 } 19 this._resolverThunk = (() => resolver); 20 } 21 22 public IController Create(RequestContext requestContext, Type controllerType) 23 { 24 IController result; 25 try 26 { 27 result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType)); 28 } 29 catch (Exception innerException) 30 { 31 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[] 32 { 33 controllerType 34 }), innerException); 35 } 36 return result; 37 } 38 } 39 40 private static readonly ConcurrentDictionary<Type, SessionStateBehavior> _sessionStateCache = new ConcurrentDictionary<Type, SessionStateBehavior>(); 41 42 private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache(); 43 44 private IBuildManager _buildManager; 45 46 private IResolver<IControllerActivator> _activatorResolver; 47 48 private IControllerActivator _controllerActivator; 49 50 private ControllerBuilder _controllerBuilder; 51 52 private ControllerTypeCache _instanceControllerTypeCache; 53 54 private IControllerActivator ControllerActivator 55 { 56 get 57 { 58 if (this._controllerActivator != null) 59 { 60 return this._controllerActivator; 61 } 62 this._controllerActivator = this._activatorResolver.Current; 63 return this._controllerActivator; 64 } 65 } 66 67 internal IBuildManager BuildManager 68 { 69 get 70 { 71 if (this._buildManager == null) 72 { 73 this._buildManager = new BuildManagerWrapper(); 74 } 75 return this._buildManager; 76 } 77 set 78 { 79 this._buildManager = value; 80 } 81 } 82 83 internal ControllerBuilder ControllerBuilder 84 { 85 get 86 { 87 return this._controllerBuilder ?? ControllerBuilder.Current; 88 } 89 set 90 { 91 this._controllerBuilder = value; 92 } 93 } 94 95 internal ControllerTypeCache ControllerTypeCache 96 { 97 get 98 { 99 return this._instanceControllerTypeCache ?? DefaultControllerFactory._staticControllerTypeCache;100 }101 set102 {103 this._instanceControllerTypeCache = value;104 }105 }106 107 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class.</summary>108 public DefaultControllerFactory() : this(null, null, null)109 {110 }111 112 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class using a controller activator.</summary>113 /// <param name="controllerActivator">An object that implements the controller activator interface.</param>114 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null)115 {116 }117 118 internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)119 {120 if (controllerActivator != null)121 {122 this._controllerActivator = controllerActivator;123 return;124 }125 IResolver<IControllerActivator> arg_44_1 = activatorResolver;126 if (activatorResolver == null)127 {128 arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");129 }130 this._activatorResolver = arg_44_1;131 }132 133 internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes)134 {135 StringBuilder stringBuilder = new StringBuilder();136 foreach (Type current in matchingTypes)137 {138 stringBuilder.AppendLine();139 stringBuilder.Append(current.FullName);140 }141 Route route2 = route as Route;142 string message;143 if (route2 != null)144 {145 message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl, new object[]146 {147 controllerName,148 route2.Url,149 stringBuilder,150 Environment.NewLine151 });152 }153 else154 {155 message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl, new object[]156 {157 controllerName,158 stringBuilder,159 Environment.NewLine160 });161 }162 return new InvalidOperationException(message);163 }164 165 private static InvalidOperationException CreateDirectRouteAmbiguousControllerException(ICollection<Type> matchingTypes)166 {167 StringBuilder stringBuilder = new StringBuilder();168 foreach (Type current in matchingTypes)169 {170 stringBuilder.AppendLine();171 stringBuilder.Append(current.FullName);172 }173 string message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_DirectRouteAmbiguous, new object[]174 {175 stringBuilder,176 Environment.NewLine177 });178 return new InvalidOperationException(message);179 }180 181 /// <summary>Creates the specified controller by using the specified request context.</summary>182 /// <returns>The controller.</returns>183 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>184 /// <param name="controllerName">The name of the controller.</param>185 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>186 /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName" /> parameter is null or empty.</exception>187 public virtual IController CreateController(RequestContext requestContext, string controllerName)188 {189 if (requestContext == null)190 {191 throw new ArgumentNullException("requestContext");192 }193 if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())194 {195 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");196 }197 Type controllerType = this.GetControllerType(requestContext, controllerName);198 return this.GetControllerInstance(requestContext, controllerType);199 }200 201 /// <summary>Retrieves the controller instance for the specified request context and controller type.</summary>202 /// <returns>The controller instance.</returns>203 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>204 /// <param name="controllerType">The type of the controller.</param>205 /// <exception cref="T:System.Web.HttpException">206 /// <paramref name="controllerType" /> is null.</exception>207 /// <exception cref="T:System.ArgumentException">208 /// <paramref name="controllerType" /> cannot be assigned.</exception>209 /// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType" /> cannot be created.</exception>210 protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)211 {212 if (controllerType == null)213 {214 throw new HttpException(404, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[]215 {216 requestContext.HttpContext.Request.Path217 }));218 }219 if (!typeof(IController).IsAssignableFrom(controllerType))220 {221 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[]222 {223 controllerType224 }), "controllerType");225 }226 return this.ControllerActivator.Create(requestContext, controllerType);227 }228 229 /// <summary>Returns the controller's session behavior.</summary>230 /// <returns>The controller's session behavior.</returns>231 /// <param name="requestContext">The request context.</param>232 /// <param name="controllerType">The type of the controller.</param>233 protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)234 {235 if (controllerType == null)236 {237 return SessionStateBehavior.Default;238 }239 return DefaultControllerFactory._sessionStateCache.GetOrAdd(controllerType, delegate(Type type)240 {241 SessionStateAttribute sessionStateAttribute = type.GetCustomAttributes(typeof(SessionStateAttribute), true).OfType<SessionStateAttribute>().FirstOrDefault<SessionStateAttribute>();242 if (sessionStateAttribute == null)243 {244 return SessionStateBehavior.Default;245 }246 return sessionStateAttribute.Behavior;247 });248 }249 250 /// <summary>Retrieves the controller type for the specified name and request context.</summary>251 /// <returns>The controller type.</returns>252 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>253 /// <param name="controllerName">The name of the controller.</param>254 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)255 {256 if (requestContext == null)257 {258 throw new ArgumentNullException("requestContext");259 }260 if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))261 {262 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");263 }264 RouteData routeData = requestContext.RouteData;265 if (routeData != null && routeData.HasDirectRouteMatch())266 {267 return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);268 }269 object obj;270 if (routeData.DataTokens.TryGetValue("Namespaces", out obj))271 {272 IEnumerable<string> enumerable = obj as IEnumerable<string>;273 if (enumerable != null && enumerable.Any<string>())274 {275 HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase);276 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);277 if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))278 {279 return controllerTypeWithinNamespaces;280 }281 }282 }283 if (this.ControllerBuilder.DefaultNamespaces.Count > 0)284 {285 HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);286 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);287 if (controllerTypeWithinNamespaces != null)288 {289 return controllerTypeWithinNamespaces;290 }291 }292 return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);293 }294 295 private static Type GetControllerTypeFromDirectRoute(RouteData routeData)296 {297 IEnumerable<RouteData> directRouteMatches = routeData.GetDirectRouteMatches();298 List<Type> list = new List<Type>();299 foreach (RouteData current in directRouteMatches)300 {301 if (current != null)302 {303 Type targetControllerType = current.GetTargetControllerType();304 if (targetControllerType == null)305 {306 throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerType);307 }308 if (!list.Contains(targetControllerType))309 {310 list.Add(targetControllerType);311 }312 }313 }314 if (list.Count == 0)315 {316 return null;317 }318 if (list.Count == 1)319 {320 return list[0];321 }322 throw DefaultControllerFactory.CreateDirectRouteAmbiguousControllerException(list);323 }324 325 private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)326 {327 this.ControllerTypeCache.EnsureInitialized(this.BuildManager);328 ICollection<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces);329 switch (controllerTypes.Count)330 {331 case 0:332 return null;333 case 1:334 return controllerTypes.First<Type>();335 default:336 throw DefaultControllerFactory.CreateAmbiguousControllerException(route, controllerName, controllerTypes);337 }338 }339 340 /// <summary>Releases the specified controller.</summary>341 /// <param name="controller">The controller to release.</param>342 public virtual void ReleaseController(IController controller)343 {344 IDisposable disposable = controller as IDisposable;345 if (disposable != null)346 {347 disposable.Dispose();348 }349 }350 351 internal IReadOnlyList<Type> GetControllerTypes()352 {353 this.ControllerTypeCache.EnsureInitialized(this.BuildManager);354 return this.ControllerTypeCache.GetControllerTypes();355 }356 357 /// <summary>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.</summary>358 /// <returns>The controller's session behavior.</returns>359 /// <param name="requestContext">The request context.</param>360 /// <param name="controllerName">The controller name.</param>361 SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)362 {363 if (requestContext == null)364 {365 throw new ArgumentNullException("requestContext");366 }367 if (string.IsNullOrEmpty(controllerName))368 {369 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");370 }371 Type controllerType = this.GetControllerType(requestContext, controllerName);372 return this.GetControllerSessionBehavior(requestContext, controllerType);373 }374 }
既然是ControllerFactory,DefaultControllerFactory肯定也实现了IControllerFactory接口,别的我们就不看了,我们看看是如何创建Controller对象的,方法代码如下:
1 public virtual IController CreateController(RequestContext requestContext, string controllerName) 2 { 3 if (requestContext == null) 4 { 5 throw new ArgumentNullException("requestContext"); 6 } 7 if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch()) 8 { 9 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");10 }11 Type controllerType = this.GetControllerType(requestContext, controllerName);12 return this.GetControllerInstance(requestContext, controllerType);13 }
代码很简单,该方法获取Controller的Type类型对象,然后根据Type对象创建实例。这个方法里面有两个辅助方法,一个是GetControllerType方法,另一个是GetControllerInstance方法,根据名称我们就能知道是做什么的。我们先看看GetControllerType方法的源码吧,这里是关键,没有Type对象的获取,以后都是空言:
1 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) 2 { 3 if (requestContext == null) 4 { 5 throw new ArgumentNullException("requestContext"); 6 } 7 if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch())) 8 { 9 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");10 }11 RouteData routeData = requestContext.RouteData;12 if (routeData != null && routeData.HasDirectRouteMatch())13 {14 return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);15 }16 object obj;17 if (routeData.DataTokens.TryGetValue("Namespaces", out obj))18 {19 IEnumerable<string> enumerable = obj as IEnumerable<string>;20 if (enumerable != null && enumerable.Any<string>())21 {22 HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase);23 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);24 if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))25 {26 return controllerTypeWithinNamespaces;27 }28 }29 }30 if (this.ControllerBuilder.DefaultNamespaces.Count > 0)31 {32 HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);33 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);34 if (controllerTypeWithinNamespaces != null)35 {36 return controllerTypeWithinNamespaces;37 }38 }39 return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);40 }
我们先根据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激活系统中肯定也包含着扩展点,下来我们一一详述。
四、小结
我们可以小结了,到此,Controler激活系统这个小节我就写完了。其实不是很复杂,大家在看的时候,刚开始别太关注代码的细节,先把握整个数据流向,或者叫请求脉络,把里面所涉及到的对象和关系都整理清楚了,然后再有针对性的去看涉及到的每个对象,不是画画图,不是画画,是画各个对象之间的关系图,图能画出来,说明你的心中就有了整体把握了。如果太关注细节,太关注某个类型的代码,也会很容易顾此失彼,因为我开始就是那样,总是看了后面忘记前面,请求的线路也不是很清楚。所以我们学习的时候也要有方法,方法对,学的就快,成就感就能很快产生。好了,说了这么多了,继续努力吧,希望我写的东西对大家有所帮助。
- 白话ASP.NET MVC之三:Controller是如何解析出来的
- ASP.NET MVC的View是如何被呈现出来的?
- 学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(三)
- 学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(三)
- asp.net mvc 之 Controller 和 Action
- 从零开始学习 ASP.NET MVC 1.0 (三) Controller/Action 深入解析与应用实例
- 从零开始学习 ASP.NET MVC 1.0 (三) Controller/Action 深入解析与应用实例
- ASP.NET MVC Controller的激活机制
- ASP.NET MVC Controller的激活
- ASP.NET MVC Controller的激活
- Asp.net Mvc Framework 三 (Controller与View)
- Asp.net Mvc Framework 三 (Controller与View)
- asp.net MVC Controller 小记
- Asp.net MVC Controller学习
- Asp.net mvc中controller与view间的如何传递数据
- 如何在多个项目中分离Asp.Net Core Mvc的Controller和Areas
- Asp.net MVC 示例项目"Suteki.Shop"分析之---Controller
- Asp.net MVC 示例项目"Suteki.Shop"分析之---Controller
- Callable与Future
- Character 类
- 今日头条2017秋招前端工程师笔试试卷
- Java使用POI导出Word文档
- 连接Oracle数据库——接触oracle的第二天
- 白话ASP.NET MVC之三:Controller是如何解析出来的
- 第四章 Thread Executors(Executors多线程架构)【中】
- linux(cenOS6.5)卸载自带openJDK并安装JDK
- android view surfaceView GLSurfaceView
- Java基础知识之容器(一)
- ArcGIS空间分析工具
- Exception in thread "main" java.io.FileNotFoundException: E:\java\.metadata (拒绝访问。)的理解
- 1050. 螺旋矩阵(25)
- HDU 6121 树的节点计数