我们在上一篇文章中介绍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 private static readonly object _requestDataKey = new object(); 7 8 private RouteCollection _routeCollection; 9 10 public RouteCollection RouteCollection11 {12 get13 {14 if (this._routeCollection == null)15 {16 this._routeCollection = RouteTable.Routes;17 }18 return this._routeCollection;19 }20 set21 {22 this._routeCollection = value;23 }24 }25 26 protected virtual void Dispose()27 {28 }29 30 protected virtual void Init(HttpApplication application)31 {32 if (application.Context.Items[UrlRoutingModule._contextKey] != null)33 {34 return;35 }36 application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;37 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);38 }39 40 private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)41 {42 HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);43 this.PostResolveRequestCache(context);44 }45 46 [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]47 public virtual void PostMapRequestHandler(HttpContextBase context)48 {49 }50 51 public virtual void PostResolveRequestCache(HttpContextBase context)52 {53 RouteData routeData = this.RouteCollection.GetRouteData(context);54 if (routeData == null)55 {56 return;57 }58 IRouteHandler routeHandler = routeData.RouteHandler;59 if (routeHandler == null)60 {61 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));62 }63 if (routeHandler is StopRoutingHandler)64 {65 return;66 }67 RequestContext requestContext = new RequestContext(context, routeData);68 context.Request.RequestContext = requestContext;69 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);70 if (httpHandler == null)71 {72 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]73 {74 routeHandler.GetType()75 }));76 }77 if (!(httpHandler is UrlAuthFailureHandler))78 {79 context.RemapHandler(httpHandler);80 return;81 }82 if (FormsAuthenticationModule.FormsAuthRequired)83 {84 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);85 return;86 }87 throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));88 }89 90 void IHttpModule.Dispose()91 {92 this.Dispose();93 }94 95 void IHttpModule.Init(HttpApplication application)96 {97 this.Init(application);98 }99 }
具体来说,该组件通过以RouteTable的静态属性Routes表示的路由表针对当前请求实施路由解析,如果有匹配,就会根据路由对象Route来生成RouteData路由数据对象,然后我们借助RouteData对象的RouteHandler属性获取想要的HttpHandler对象。在默认情况下这个RouteHandler属性所代表的对象是MvcRouteHandler。翠花,上代码:
1 ///Creates an object that implements the IHttpHandler interface and passes the request context to it. 2 public class MvcRouteHandler : IRouteHandler 3 { 4 private IControllerFactory _controllerFactory; 5 6 ///Initializes a new instance of the 7 public MvcRouteHandler() 8 { 9 }10 11 ///class. Initializes a new instance of the 12 /// The controller factory.13 public MvcRouteHandler(IControllerFactory controllerFactory)14 {15 this._controllerFactory = controllerFactory;16 }17 18 ///class using the specified factory controller object. Returns the HTTP handler by using the specified HTTP context. 19 ///The HTTP handler. 20 /// The request context.21 protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)22 {23 requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));24 return new MvcHandler(requestContext);25 }26 27 ///Returns the session behavior. 28 ///The session behavior. 29 /// The request context.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 ///Returns the HTTP handler by using the specified request context. 42 ///The HTTP handler. 43 /// The request context.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接口定义如下:
public interface IRouteHandler{ IHttpHandler GetHttpHandler(RequestContext requestContext);}
MvcRouteHandler会给我们直接返回MvcHandler对象,这个对象用于处理请求,包括激活Controler对象,代码最有说服力,这份代码,上篇文章也贴过,现在也贴一下把,上代码:
1 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState 2 { 3 private struct ProcessRequestState 4 { 5 internal IAsyncController AsyncController; 6 7 internal IControllerFactory Factory; 8 9 internal RequestContext RequestContext; 10 11 internal void ReleaseController() 12 { 13 this.Factory.ReleaseController(this.AsyncController); 14 } 15 } 16 17 private static readonly object _processRequestTag = new object(); 18 19 internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString(); 20 21 ///Contains the header name of the ASP.NET MVC version. 22 public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version"; 23 24 private ControllerBuilder _controllerBuilder; 25 26 internal ControllerBuilder ControllerBuilder 27 { 28 get 29 { 30 if (this._controllerBuilder == null) 31 { 32 this._controllerBuilder = ControllerBuilder.Current; 33 } 34 return this._controllerBuilder; 35 } 36 set 37 { 38 this._controllerBuilder = value; 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 public static bool DisableMvcResponseHeader 45 { 46 get; 47 set; 48 } 49 50 ///Gets a value that indicates whether another request can use the 51 ///instance. true if the 52 protected virtual bool IsReusable 53 { 54 get 55 { 56 return false; 57 } 58 } 59 60 ///instance is reusable; otherwise, false. Gets the request context. 61 ///The request context. 62 public RequestContext RequestContext 63 { 64 get; 65 private set; 66 } 67 68 ///Gets a value that indicates whether another request can use the 69 ///instance. true if the 70 bool IHttpHandler.IsReusable 71 { 72 get 73 { 74 return this.IsReusable; 75 } 76 } 77 78 ///instance is reusable; otherwise, false. Initializes a new instance of the 79 /// The request context. 80 ///class. The 81 public MvcHandler(RequestContext requestContext) 82 { 83 if (requestContext == null) 84 { 85 throw new ArgumentNullException("requestContext"); 86 } 87 this.RequestContext = requestContext; 88 } 89 90 ///parameter is null. Adds the version header by using the specified HTTP context. 91 /// The HTTP context. 92 protected internal virtual void 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 /// The HTTP context.103 /// The asynchronous callback method.104 /// The state of the asynchronous object.105 protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)106 {107 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);108 return this.BeginProcessRequest(httpContext2, callback, state);109 }110 111 ///Called by ASP.NET to begin asynchronous request processing using the base HTTP context. 112 ///The status of the asynchronous call. 113 /// The HTTP context.114 /// The asynchronous callback method.115 /// The state of the asynchronous object.116 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)117 {118 IController controller;119 IControllerFactory factory;120 this.ProcessRequestInit(httpContext, out controller, out factory);121 IAsyncController asyncController = controller as IAsyncController;122 if (asyncController != null)123 {124 BeginInvokeDelegatebeginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)125 {126 IAsyncResult result;127 try128 {129 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);130 }131 catch132 {133 innerState.ReleaseController();134 throw;135 }136 return result;137 };138 EndInvokeVoidDelegate endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)139 {140 try141 {142 innerState.AsyncController.EndExecute(asyncResult);143 }144 finally145 {146 innerState.ReleaseController();147 }148 };149 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState150 {151 AsyncController = asyncController,152 Factory = factory,153 RequestContext = this.RequestContext154 };155 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();156 return AsyncResultWrapper.Begin (callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);157 }158 Action action = delegate159 {160 try161 {162 controller.Execute(this.RequestContext);163 }164 finally165 {166 factory.ReleaseController(controller);167 }168 };169 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);170 }171 172 /// Called by ASP.NET when asynchronous request processing has ended. 173 /// The asynchronous result.174 protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)175 {176 AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag);177 }178 179 private static string GetMvcVersionString()180 {181 return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);182 }183 184 ///Processes the request by using the specified HTTP request context. 185 /// The HTTP context.186 protected virtual void ProcessRequest(HttpContext httpContext)187 {188 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);189 this.ProcessRequest(httpContext2);190 }191 192 ///Processes the request by using the specified base HTTP request context. 193 /// The HTTP context.194 protected internal virtual void ProcessRequest(HttpContextBase httpContext)195 {196 IController controller;197 IControllerFactory controllerFactory;198 this.ProcessRequestInit(httpContext, out controller, out controllerFactory);199 try200 {201 controller.Execute(this.RequestContext);202 }203 finally204 {205 controllerFactory.ReleaseController(controller);206 }207 }208 209 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)210 {211 HttpContext current = HttpContext.Current;212 if (current != null && ValidationUtility.IsValidationEnabled(current) == true)213 {214 ValidationUtility.EnableDynamicValidation(current);215 }216 this.AddVersionHeader(httpContext);217 this.RemoveOptionalRoutingParameters();218 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");219 factory = this.ControllerBuilder.GetControllerFactory();220 controller = factory.CreateController(this.RequestContext, requiredString);221 if (controller == null)222 {223 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]224 {225 factory.GetType(),226 requiredString227 }));228 }229 }230 231 private void RemoveOptionalRoutingParameters()232 {233 RouteValueDictionary values = this.RequestContext.RouteData.Values;234 values.RemoveFromDictionary((KeyValuePairentry) => entry.Value == UrlParameter.Optional);235 }236 237 /// Enables processing of HTTP Web requests by a custom HTTP handler that implements the 238 /// Aninterface. object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) that are used to service HTTP requests.239 void IHttpHandler.ProcessRequest(HttpContext httpContext)240 {241 this.ProcessRequest(httpContext);242 }243 244 /// Called by ASP.NET to begin asynchronous request processing using the base HTTP context. 245 ///The status of the asynchronous call. 246 /// The HTTP context.247 /// The asynchronous callback method.248 /// The data.249 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)250 {251 return this.BeginProcessRequest(context, cb, extraData);252 }253 254 ///Called by ASP.NET when asynchronous request processing has ended. 255 /// The asynchronous result.256 void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)257 {258 this.EndProcessRequest(result);259 }260 }
1 ///Called by ASP.NET to begin asynchronous request processing using the base HTTP context. 2 ///The status of the asynchronous call. 3 /// The HTTP context. 4 /// The asynchronous callback method. 5 /// The state of the asynchronous object. 6 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 7 { 8 IController controller; 9 IControllerFactory factory;10 this.ProcessRequestInit(httpContext, out controller, out factory);11 IAsyncController asyncController = controller as IAsyncController;12 if (asyncController != null)13 {14 BeginInvokeDelegatebeginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)15 {16 IAsyncResult result;17 try18 {19 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);20 }21 catch22 {23 innerState.ReleaseController();24 throw;25 }26 return result;27 };28 EndInvokeVoidDelegate endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)29 {30 try31 {32 innerState.AsyncController.EndExecute(asyncResult);33 }34 finally35 {36 innerState.ReleaseController();37 }38 };39 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState40 {41 AsyncController = asyncController,42 Factory = factory,43 RequestContext = this.RequestContext44 };45 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();46 return AsyncResultWrapper.Begin (callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);47 }48 Action action = delegate49 {50 try51 {52 controller.Execute(this.RequestContext);53 }54 finally55 {56 factory.ReleaseController(controller);57 }58 };59 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);60 }
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 ///Represents the controller factory that is registered by default. 2 public class DefaultControllerFactory : IControllerFactory 3 { 4 private class DefaultControllerActivator : IControllerActivator 5 { 6 private Func_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 _sessionStateCache = new ConcurrentDictionary (); 41 42 private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache(); 43 44 private IBuildManager _buildManager; 45 46 private IResolver _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 /// Initializes a new instance of the 108 public DefaultControllerFactory() : this(null, null, null)109 {110 }111 112 ///class. Initializes a new instance of the 113 /// An object that implements the controller activator interface.114 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null)115 {116 }117 118 internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolverclass using a controller activator. activatorResolver, IDependencyResolver dependencyResolver)119 {120 if (controllerActivator != null)121 {122 this._controllerActivator = controllerActivator;123 return;124 }125 IResolver arg_44_1 = activatorResolver;126 if (activatorResolver == null)127 {128 arg_44_1 = new SingleServiceResolver (() => 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 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 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 /// 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.184 /// The name of the controller.185 ///The 186 ///parameter is null. The 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 ///parameter is null or empty. Retrieves the controller instance for the specified request context and controller type. 202 ///The controller instance. 203 /// The context of the HTTP request, which includes the HTTP context and route data.204 /// The type of the controller.205 ///206 /// 207 ///is null. 208 /// 209 ///cannot be assigned. An instance of 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 ///cannot be created. Returns the controller's session behavior. 230 ///The controller's session behavior. 231 /// The request context.232 /// The type of the controller.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().FirstOrDefault ();242 if (sessionStateAttribute == null)243 {244 return SessionStateBehavior.Default;245 }246 return sessionStateAttribute.Behavior;247 });248 }249 250 /// Retrieves the controller type for the specified name and request context. 251 ///The controller type. 252 /// The context of the HTTP request, which includes the HTTP context and route data.253 /// The name of the controller.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 IEnumerableenumerable = obj as IEnumerable ;273 if (enumerable != null && enumerable.Any ())274 {275 HashSet namespaces = new HashSet (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 namespaces2 = new HashSet (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 directRouteMatches = routeData.GetDirectRouteMatches();298 List list = new List ();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 namespaces)326 {327 this.ControllerTypeCache.EnsureInitialized(this.BuildManager);328 ICollection controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces);329 switch (controllerTypes.Count)330 {331 case 0:332 return null;333 case 1:334 return controllerTypes.First ();335 default:336 throw DefaultControllerFactory.CreateAmbiguousControllerException(route, controllerName, controllerTypes);337 }338 }339 340 /// Releases the specified controller. 341 /// The controller to release.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 IReadOnlyListGetControllerTypes()352 {353 this.ControllerTypeCache.EnsureInitialized(this.BuildManager);354 return this.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 358 ///method. The controller's session behavior. 359 /// The request context.360 /// The controller name.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 }
1 ///Creates the specified controller by using the specified request context. 2 ///The controller. 3 /// The context of the HTTP request, which includes the HTTP context and route data. 4 /// The name of the controller. 5 ///The 6 ///parameter is null. The 7 public virtual IController CreateController(RequestContext requestContext, string controllerName) 8 { 9 if (requestContext == null)10 {11 throw new ArgumentNullException("requestContext");12 }13 if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())14 {15 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");16 }17 Type controllerType = this.GetControllerType(requestContext, controllerName);18 return this.GetControllerInstance(requestContext, controllerType);19 }parameter is null or empty.
1 ///Retrieves the controller type for the specified name and request context. 2 ///The controller type. 3 /// The context of the HTTP request, which includes the HTTP context and route data. 4 /// The name of the controller. 5 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) 6 { 7 if (requestContext == null) 8 { 9 throw new ArgumentNullException("requestContext");10 }11 if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))12 {13 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");14 }15 RouteData routeData = requestContext.RouteData;16 if (routeData != null && routeData.HasDirectRouteMatch())17 {18 return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);19 }20 object obj;21 if (routeData.DataTokens.TryGetValue("Namespaces", out obj))22 {23 IEnumerableenumerable = obj as IEnumerable ;24 if (enumerable != null && enumerable.Any ())25 {26 HashSet namespaces = new HashSet (enumerable, StringComparer.OrdinalIgnoreCase);27 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);28 if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))29 {30 return controllerTypeWithinNamespaces;31 }32 }33 }34 if (this.ControllerBuilder.DefaultNamespaces.Count > 0)35 {36 HashSet namespaces2 = new HashSet (this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);37 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);38 if (controllerTypeWithinNamespaces != null)39 {40 return controllerTypeWithinNamespaces;41 }42 }43 return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);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 internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
其实这也是一个扩展点,但是我们今天不扩展这里,因为IOC框架和这里的关系不大,我们要扩展的是第二受保护的虚方法,方法签名如下:
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
这个方法是用于创建Controller实例的,这里正好可以使用IOC容器来管理Controller实例,当然在DefaultControllerFactory类型中还有其他的扩展点,大家可以自己去看,全部代码我以前也贴出来过。
1 public class UnityControllerFactory:DefaultControllerFactory 2 { 3 ///4 /// IOC的容器对象 5 /// 6 public IUnityContainer UnityContainer { get; private set; } 7 8 ///9 /// 通过构造函数初始化IOC的容器对象10 /// 11 /// 传入的IOC容器对象12 public UnityControllerFactory(IUnityContainer unityContainer)13 {14 if (unityContainer != null)15 {16 this.UnityContainer = unityContainer;17 }18 else19 {20 throw new ArgumentNullException("容器对象不能为空!");21 }22 }23 24 ///25 /// 26 /// 27 /// 28 /// 29 ///30 protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)31 {32 if (controllerType == null)33 {34 return null;35 }36 return (IController)this.UnityContainer.Resolve(controllerType);37 }38 }
///Provides fine-grained control over how controllers are instantiated using dependency injection. public interface IControllerActivator{ ///When implemented in a class, creates a controller. ///The created controller. /// The request context. /// The controller type. IController Create(RequestContext requestContext, Type controllerType);}
该接口中定义了Create方法实现针对目标Controller对象的创建,两个参数分别表示请求上下文和Controller的类型。我们在看看DefaultControllerFactory和ControllerActivator之间的关系吧,DefaultControllerFactory类型有三个构造函数,代码如下:
1 ///Initializes a new instance of the 2 public DefaultControllerFactory() : this(null, null, null) 3 { 4 } 5 6 ///class. Initializes a new instance of the 7 /// An object that implements the controller activator interface. 8 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null) 9 {10 }11 12 internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolverclass using a controller activator. activatorResolver, IDependencyResolver dependencyResolver)13 {14 if (controllerActivator != null)15 {16 this._controllerActivator = controllerActivator;17 return;18 }19 IResolver arg_44_1 = activatorResolver;20 if (activatorResolver == null)21 {22 arg_44_1 = new SingleServiceResolver (() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");23 }24 this._activatorResolver = arg_44_1;25 }
1 public class NinjectControllerActivator : IControllerActivator 2 { 3 public IKernel Kernel { get; private set; } 4 5 public NinjectControllerActivator() 6 { 7 this.Kernel = new StandardKernel(); 8 } 9 public IController Create(RequestContext requestContext, Type controllerType)10 {11 return (IController)this.Kernel.TryGet(controllerType);12 }13 14 public void Register() where TTo : TFrom15 {16 this.Kernel.Bind ().To ();17 }18 }
扩展写好了,还需要在Global.asax文件里面进行注册,否则不能使用。IOC相关的内容就不介绍了,大家可以自行研究。
扩展点三:扩展DependencyResolver 如果我们在构建DefaultControllerFactory对象的时候没有显示指定采用的ControllerActiviator参数,则它会使用默认的一个ControllerActivator对象DefaultControllerActivator来完成,该类型是一个私有类型,外界无法访问。源码如下:1 private class DefaultControllerActivator : IControllerActivator 2 { 3 private Func_resolverThunk; 4 5 public DefaultControllerActivator() : this(null) 6 { 7 } 8 9 public DefaultControllerActivator(IDependencyResolver resolver)10 {11 if (resolver == null)12 {13 this._resolverThunk = (() => DependencyResolver.Current);14 return;15 }16 this._resolverThunk = (() => resolver);17 }18 19 public IController Create(RequestContext requestContext, Type controllerType)20 {21 IController result;22 try23 {24 result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));25 }26 catch (Exception innerException)27 {28 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]29 {30 controllerType31 }), innerException);32 }33 return result;34 }35 }
其实DefaultControllerActivator对象要像创建目标Controller对象,还依赖另外一个叫DependencyResolver的对象。所有的DenpendencyResolver类型均实现了IDenpendencyResolver接口,接口定义如下:
1 ///Defines the methods that simplify service location and dependency resolution. 2 public interface IDependencyResolver 3 { 4 ///Resolves singly registered services that support arbitrary object creation. 5 ///The requested service or object. 6 /// The type of the requested service or object. 7 object GetService(Type serviceType); 8 9 ///Resolves multiply registered services. 10 ///The requested services. 11 /// The type of the requested services.12 IEnumerable
系统中使用的DependencyResolver是通过DependencyResolver类型注册的。DependencyResolver类型有一个叫做Current的静态属性,该属性表示的是当前的、IDependencyResolver类型的对象。我们可以通过静态方法SetResolver来注册IDependencyResolver对象。需要说明一点,还有静态方法的DependencyResolver类型并没有实现IDependencyResolver接口,所以说该类型并不是真正的DependencyResolver类型,我们可以把它理解为一个Facade,它汇集了所要使用的对象和方法而已,使用更方便。
如果我们没有对DependencyResolver进行显示的注册,则系统会使用一个默认的叫做DefaultDependencyResolver的对象,本对象也是一个私有类型,外界无法使用。 当我们通过无参的构造函数或者通过将ControllerActivator参数指定为null的构造函数来创建DefaultControllerFactory实例的时候,那么用于激活Controller实例的将是通过DependencyResolver类型的静态属性Current表示的DependencyResolver对象来激活的,我们的扩展终于也找到了。 最后实现的代码如下:1 public class NinjectDependencyResolver : IDependencyResolver 2 { 3 public IKernel Kernel { get; private set; } 4 5 public NinjectDependencyResolver() 6 { 7 this.Kernel = new StandardKernel(); 8 } 9 10 public void Register() where TTo : TFrom11 {12 this.Kernel.Bind ().To ();13 }14 15 public object GetService(Type serviceType)16 {17 return this.Kernel.TryGet(serviceType);18 }19 20 public IEnumerable