微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

白话ASP.NET MVC之三:Controller是如何解析出来的

    我们在上一篇文章中介绍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激活系统这个小节我就写完了。其实不是很复杂,大家在看的时候,刚开始别太关注代码的细节,先把握整个数据流向,或者叫请求脉络,把里面所涉及到的对象和关系都整理清楚了,然后再有针对性的去看涉及到的每个对象,不是画画图,不是画画,是画各个对象之间的关系图,图能画出来,说明你的心中就有了整体把握了。如果太关注细节,太关注某个类型的代码,也会很容易顾此失彼,因为我开始就是那样,总是看了后面忘记前面,请求的线路也不是很清楚。所以我们学习的时候也要有方法方法对,学的就快,成就感就能很快产生。好了,说了这么多了,继续努力吧,希望我写的东西对大家有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐