前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了。我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中注册的路由对象一个一个的比较,如果没有找到就返回Null,请求就到此终止了。如果有匹配的路由对象,选择第一个匹配的Route对象,并且根据该Route对象,生成了路由数据RouteData对象,本对象是为了封装成RequestContext对象,RequestContext对象封装了RouteData和HttpContext对象。
其实RequestContext对象也是作为参数使用的,我们根据Route对象获得了RouteData,该对象有一个RouteHandler属性,这个属性的值在ASP.NET MVC中是MvcRouteHandler。RouteHandler主要的作用是提供用于处理最终请求的HttpHandler对象,代码如下:
IHttpHandler=RouteData.RouteHandler.GetHttpHandler(requestContext);
HttpContext.Remap(IHttpHandler);
上文中我们获取到的RequestContext,将此作为参数调用RouteHandler的GetHttpHandler方法,我们获得了IHttpHandler对象,我们需要把请求交给这个HttpHandler来处理,通过HttpContext.Remap(Handler)实现请求的交接,这个Handler在ASP.NET MVC中就是MvcHandler,我们既然获得了HttpHandler对象,也实现了请求的交接,下一步该做什么呢?
一、概览
我们获得了用于处理请求HttpHandler对象,下一步最重要的任务是把Controller对象找到,RouteData对象的Values属性的Controller键和Action键的值只是一个字符串的结果,我们要通过这个名称找到具体的Controller类型实例,才能执行Action方法,给客户想要的应答。这篇文章就是Controller的激活方法的详解。
ASP.NET MVC系统真的很庞大,也很复杂,如果我们一上来就瀑布式的说,就像流水账一样,估计也不太容易说明白。为了便于理解,我们人为的把ASP.NET MVC这么庞大的系统拆分成相互独立,又有一定联系的多个子系统,然后我们各个击破,理解起来也就简单了,最后在把各个部分整合在一起,理解就全面了。今天我们要说的这一部分暂时叫做Controller激活系统吧,激活系统有两个含义,一是我们要找到我们需要的Controller对象,并实例化;二是我们药缓存他,并要执行它。
我们先来看看MvcHandler的源码吧,有助于我们的理解,代码如下:
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 try 129 { 130 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState); 131 } 132 catch 133 { 134 innerState.ReleaseController(); 135 throw; 136 } 137 return result; 138 }; 139 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState) 140 { 141 try 142 { 143 innerState.AsyncController.EndExecute(asyncResult); 144 } 145 finally 146 { 147 innerState.ReleaseController(); 148 } 149 }; 150 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState 151 { 152 AsyncController = asyncController, 153 Factory = factory, 154 RequestContext = this.RequestContext 155 }; 156 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext(); 157 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext); 158 } 159 Action action = delegate 160 { 161 try 162 { 163 controller.Execute(this.RequestContext); 164 } 165 finally 166 { 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 try 201 { 202 controller.Execute(this.RequestContext); 203 } 204 finally 205 { 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 { 215 ValidationUtility.EnableDynamicValidation(current); 216 } 217 this.AddVersionHeader(httpContext); 218 this.RemoveOptionalRoutingParameters(); 219 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); 220 factory = this.ControllerBuilder.GetControllerFactory(); 221 controller = factory.CreateController(this.RequestContext, requiredString); 222 if (controller == null) 223 { 224 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] 225 { 226 factory.GetType(), 227 requiredString 228 })); 229 } 230 } 231 232 private void RemoveOptionalRoutingParameters() 233 { 234 RouteValueDictionary values = this.RequestContext.RouteData.Values; 235 values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional); 236 } 237 238 /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary> 239 /// <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> 240 void IHttpHandler.ProcessRequest(HttpContext httpContext) 241 { 242 this.ProcessRequest(httpContext); 243 } 244 245 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary> 246 /// <returns>The status of the asynchronous call.</returns> 247 /// <param name="context">The HTTP context.</param> 248 /// <param name="cb">The asynchronous callback method.</param> 249 /// <param name="extraData">The data.</param> 250 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) 251 { 252 return this.BeginProcessRequest(context, cb, extraData); 253 } 254 255 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary> 256 /// <param name="result">The asynchronous result.</param> 257 void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) 258 { 259 this.EndProcessRequest(result); 260 } 261 }