你真的熟悉ASP.NET MVC的整個生命周期嗎?
- 2021 年 8 月 23 日
- 筆記
- ASP.NET MVC, httpapplication, MVC, mvchandler, UrlRoutingModule, 生命周期
一、介紹
我們做開發的,尤其是做微軟技術棧的,有一個方向是跳不過去的,那就是MVC開發。我相信大家,做ASP.NET MVC 開發有的有很長時間,當然,也有剛進入這個行業的。無論如何,如果有人問你,你知道ASP.NET MVC的生命周期嗎?你知道它的來世今生嗎?你知道它和 ASP.NET WEBFORM 有什麼區別嗎?估計,這些問題,有很多人會答不上來,或者說不清楚。今天,我就把我的理解寫出來,也是對我自己學習的一次回顧和總結吧。當然,由於本人能力有限,在寫的過程中也可能會有一些錯誤,希望大家多多包涵,當然,更希望大家能不靈賜教,我們共同進步。
在開始之前,我們先來說說,ASP.NET Web Form 和 Asp.net MVC 有什麼區別,這裡說的區別,當然是本質區別,不是適用語法那個層次的。其實,說起來,ASP.NET WEB FORM 和 ASP.NET MVC 它們兩個沒有本質區別,使用的都是ASP.NET WEB FORM 的管道處理模型,ASP.NET MVC 也是通過擴展 IHttpModule 和 IHttpHandler 來實現的,都是基於 ASP.NET 的 HttpApplication 的管道處理模型擴展的,在這個層面來說,它們是一樣的。當然,大家不要抬杠,我說的本質區別都是在這個方面,不同意的勿噴。
有人會問,ASP.NET MVC 和 ASP.NET WEBAPI 它們會有什麼不同嗎?好像 WebAPi 能做的,WebMVC都可以完成,第一眼看上去,好像是這樣,但是它們有著本質的不同。WebAPI 的處理管道是重新寫過的,不是基於 HTTPApplication 管道擴展的。ASP.NET WEB API 類似專人做專事,它的管道處理模型更高效,並且有了 Restfull 的概念。當然,大家如何向了解更細的內容,就需要看源碼了。或再說回來,到了 NET CORE 時代,二者又融合管道了。
二、MVC生命周期詳述
1、我們既然要說 ASP.NET MVC的生命周期,為了給大家一個整體印象,俗話說,文不如圖,我就貼一張圖,按著箭頭走,相信大家也會不能理解。
2、上圖很簡單,大家按著箭頭走,也能理解的差不多。以下是按著我的理解,劃分了4個模組。
(1)、路由模組
RouteBase 是對路由規則的抽象,也就是說,一個 RouteBase 對象,也就代表了一個條 路由規則。在 ASP.NET MVC 中,有一個唯一的子類實現就是 Route ,它同樣也是路由規則的代表。我們有了路由規則,一定會把這個規則存放在一個地方,這個地方保存了很多路由規則,這個地方就是 RouteCollection,中文叫「路由集合」,因為這個集合裡面包含的就是 RouteBase 對象。
RouteCollection 就是路由集合,用於保存路由規則對象,它的定義形式:
1 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] 2 public class RouteCollection : Collection<RouteBase> 3 { 4 private class ReadLockDisposable : IDisposable 5 { 6 private ReaderWriterLockSlim _rwLock; 7 8 public ReadLockDisposable(ReaderWriterLockSlim rwLock) 9 { 10 this._rwLock = rwLock; 11 } 12 13 void IDisposable.Dispose() 14 { 15 this._rwLock.ExitReadLock(); 16 } 17 } 18 ......
RouteTable 就是路由表,其實它和 RouteCollection 是一樣的。
1 public class RouteTable 2 { 3 private static RouteCollection _instance = new RouteCollection(); 4 5 public static RouteCollection Routes 6 { 7 get 8 { 9 return RouteTable._instance; 10 } 11 } 12 }
在ASP.NET MVC處理管線中的第一站就是路由模組。當請求到達路由模組後,ASP.NET MVC 框架就會根據 RouteTable 中配置的路由模板來匹配當前請求以獲得對應的 Controller 和 Action 資訊。具體的匹配過程就是有UrlRoutingModule(System.Web.Routing.UrlRoutingModule)來實現的。如果遇到一個匹配的規則,就會立刻跳出下面的配置。也就是說,配置過程是有順序的,如果有一個匹配,後面就算有匹配的也不會執行的。
1 namespace System.Web.Routing 2 { 3 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] 4 public class UrlRoutingModule : IHttpModule 5 { 6 private static readonly object _contextKey = new object(); 7 8 private static readonly object _requestDataKey = new object(); 9 10 private RouteCollection _routeCollection; 11 12 public RouteCollection RouteCollection 13 { 14 get 15 { 16 if (this._routeCollection == null) 17 { 18 this._routeCollection = RouteTable.Routes; 19 } 20 return this._routeCollection; 21 } 22 set 23 { 24 this._routeCollection = value; 25 } 26 } 27 28 protected virtual void Dispose() 29 { 30 } 31 32 protected virtual void Init(HttpApplication application) 33 { 34 if (application.Context.Items[UrlRoutingModule._contextKey] != null) 35 { 36 return; 37 } 38 application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey; 39 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); 40 } 41 42 private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) 43 { 44 HttpApplication httpApplication = (HttpApplication)sender; 45 HttpContextBase context = new HttpContextWrapper(httpApplication.Context); 46 this.PostResolveRequestCache(context); 47 } 48 49 [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")] 50 public virtual void PostMapRequestHandler(HttpContextBase context) 51 { 52 } 53 54 public virtual void PostResolveRequestCache(HttpContextBase context) 55 { 56 RouteData routeData = this.RouteCollection.GetRouteData(context); 第一步匹配路由規則 57 if (routeData == null) 58 { 59 return; 60 } 61 IRouteHandler routeHandler = routeData.RouteHandler; 第二步:如有匹配,就找到RouteHandler對象,該類型的實例是:MvcRouteHandler。 62 if (routeHandler == null) 63 { 64 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); 65 } 66 if (routeHandler is StopRoutingHandler) 67 { 68 return; 69 } 70 RequestContext requestContext = new RequestContext(context, routeData); 71 context.Request.RequestContext = requestContext; 72 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);第三步,根據 RouteHandler 對象,找到最終處理請求的 IHttpHandler 的對象,該類型是 MvcHandler 73 if (httpHandler == null) 74 { 75 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] 76 { 77 routeHandler.GetType() 78 })); 79 } 80 if (!(httpHandler is UrlAuthFailureHandler)) 81 { 82 context.RemapHandler(httpHandler);第四步,有找到的 IHttpHandler 處理請求。 83 return; 84 } 85 if (FormsAuthenticationModule.FormsAuthRequired) 86 { 87 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); 88 return; 89 } 90 throw new HttpException(401, SR.GetString("Assess_Denied_Description3")); 91 } 92 93 void IHttpModule.Dispose() 94 { 95 this.Dispose(); 96 } 97 98 void IHttpModule.Init(HttpApplication application) 99 { 100 this.Init(application); 101 } 102 } 103 }
(2)、Controller 創建模組
經過了路由模組,生成了 RouteData 路由數據,它包含了根據路由規則匹配的 Controller 和 Action。有了路由數據,需要有處理器來處理請求,這個任務就交給了 RouteData 的 RouteHandler 屬性,它的類型是 IRouteHandler,它的值就是MvcRouteHandler,MvcRouteHandler 調用 GetHttpHandler 獲取處理請求的 IHttpHandler 對象,在 MVC 框架中就是 MvcHandler,詳細程式碼如下:


1 namespace System.Web.Mvc 2 { 3 /// <summary>Selects the controller that will handle an HTTP request.</summary> 4 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState 5 { 6 private struct ProcessRequestState 7 { 8 internal IAsyncController AsyncController; 9 10 internal IControllerFactory Factory; 11 12 internal RequestContext RequestContext; 13 14 internal void ReleaseController() 15 { 16 this.Factory.ReleaseController(this.AsyncController); 17 } 18 } 19 20 [CompilerGenerated] 21 [Serializable] 22 private sealed class <>c 23 { 24 public static readonly MvcHandler.<>c <>9 = new MvcHandler.<>c(); 25 26 public static BeginInvokeDelegate<MvcHandler.ProcessRequestState> <>9__20_0; 27 28 public static EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> <>9__20_1; 29 30 public static Func<KeyValuePair<string, object>, bool> <>9__26_0; 31 32 internal IAsyncResult <BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState) 33 { 34 IAsyncResult result; 35 try 36 { 37 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState); 38 } 39 catch 40 { 41 innerState.ReleaseController(); 42 throw; 43 } 44 return result; 45 } 46 47 internal void <BeginProcessRequest>b__20_1(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState) 48 { 49 try 50 { 51 innerState.AsyncController.EndExecute(asyncResult); 52 } 53 finally 54 { 55 innerState.ReleaseController(); 56 } 57 } 58 59 internal bool <RemoveOptionalRoutingParameters>b__26_0(KeyValuePair<string, object> entry) 60 { 61 return entry.Value == UrlParameter.Optional; 62 } 63 } 64 65 private static readonly object _processRequestTag = new object(); 66 67 internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString(); 68 69 /// <summary>Contains the header name of the ASP.NET MVC version.</summary> 70 public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version"; 71 72 private ControllerBuilder _controllerBuilder; 73 74 internal ControllerBuilder ControllerBuilder 75 { 76 get 77 { 78 if (this._controllerBuilder == null) 79 { 80 this._controllerBuilder = ControllerBuilder.Current; 81 } 82 return this._controllerBuilder; 83 } 84 set 85 { 86 this._controllerBuilder = value; 87 } 88 } 89 90 /// <summary>Gets or sets a value that indicates whether the MVC response header is disabled.</summary> 91 /// <returns>true if the MVC response header is disabled; otherwise, false.</returns> 92 public static bool DisableMvcResponseHeader 93 { 94 get; 95 set; 96 } 97 98 /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary> 99 /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns> 100 protected virtual bool IsReusable 101 { 102 get 103 { 104 return false; 105 } 106 } 107 108 /// <summary>Gets the request context.</summary> 109 /// <returns>The request context.</returns> 110 public RequestContext RequestContext 111 { 112 get; 113 private set; 114 } 115 116 /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary> 117 /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns> 118 bool IHttpHandler.IsReusable 119 { 120 get 121 { 122 return this.IsReusable; 123 } 124 } 125 126 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcHandler" /> class.</summary> 127 /// <param name="requestContext">The request context.</param> 128 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception> 129 public MvcHandler(RequestContext requestContext) 130 { 131 if (requestContext == null) 132 { 133 throw new ArgumentNullException("requestContext"); 134 } 135 this.RequestContext = requestContext; 136 } 137 138 /// <summary>Adds the version header by using the specified HTTP context.</summary> 139 /// <param name="httpContext">The HTTP context.</param> 140 protected internal virtual void AddVersionHeader(HttpContextBase httpContext) 141 { 142 if (!MvcHandler.DisableMvcResponseHeader) 143 { 144 httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName, MvcHandler.MvcVersion); 145 } 146 } 147 148 /// <summary>Called by ASP.NET to begin asynchronous request processing.</summary> 149 /// <returns>The status of the asynchronous call.</returns> 150 /// <param name="httpContext">The HTTP context.</param> 151 /// <param name="callback">The asynchronous callback method.</param> 152 /// <param name="state">The state of the asynchronous object.</param> 153 protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) 154 { 155 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); 156 return this.BeginProcessRequest(httpContext2, callback, state); 157 } 158 159 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary> 160 /// <returns>The status of the asynchronous call.</returns> 161 /// <param name="httpContext">The HTTP context.</param> 162 /// <param name="callback">The asynchronous callback method.</param> 163 /// <param name="state">The state of the asynchronous object.</param> 164 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 165 { 166 IController controller; 167 IControllerFactory factory; 168 this.ProcessRequestInit(httpContext, out controller, out factory); 169 IAsyncController asyncController = controller as IAsyncController; 170 if (asyncController != null) 171 { 172 BeginInvokeDelegate<MvcHandler.ProcessRequestState> arg_51_0; 173 if ((arg_51_0 = MvcHandler.<>c.<>9__20_0) == null) 174 { 175 arg_51_0 = (MvcHandler.<>c.<>9__20_0 = new BeginInvokeDelegate<MvcHandler.ProcessRequestState>(MvcHandler.<>c.<>9.<BeginProcessRequest>b__20_0)); 176 } 177 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = arg_51_0; 178 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> arg_71_0; 179 if ((arg_71_0 = MvcHandler.<>c.<>9__20_1) == null) 180 { 181 arg_71_0 = (MvcHandler.<>c.<>9__20_1 = new EndInvokeVoidDelegate<MvcHandler.ProcessRequestState>(MvcHandler.<>c.<>9.<BeginProcessRequest>b__20_1)); 182 } 183 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = arg_71_0; 184 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState 185 { 186 AsyncController = asyncController, 187 Factory = factory, 188 RequestContext = this.RequestContext 189 }; 190 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext(); 191 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext); 192 } 193 Action action = delegate 194 { 195 try 196 { 197 controller.Execute(this.RequestContext); 198 } 199 finally 200 { 201 factory.ReleaseController(controller); 202 } 203 }; 204 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag); 205 } 206 207 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary> 208 /// <param name="asyncResult">The asynchronous result.</param> 209 protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) 210 { 211 AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag); 212 } 213 214 private static string GetMvcVersionString() 215 { 216 return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2); 217 } 218 219 /// <summary>Processes the request by using the specified HTTP request context.</summary> 220 /// <param name="httpContext">The HTTP context.</param> 221 protected virtual void ProcessRequest(HttpContext httpContext) 222 { 223 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); 224 this.ProcessRequest(httpContext2); 225 } 226 227 /// <summary>Processes the request by using the specified base HTTP request context.</summary> 228 /// <param name="httpContext">The HTTP context.</param> 229 protected internal virtual void ProcessRequest(HttpContextBase httpContext) 230 { 231 IController controller; 232 IControllerFactory controllerFactory; 233 this.ProcessRequestInit(httpContext, out controller, out controllerFactory); 234 try 235 { 236 controller.Execute(this.RequestContext); 237 } 238 finally 239 { 240 controllerFactory.ReleaseController(controller); 241 } 242 } 243 244 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) 245 { 246 HttpContext current = HttpContext.Current; 247 if (current != null) 248 { 249 bool? flag = ValidationUtility.IsValidationEnabled(current); 250 bool flag2 = true; 251 if (flag.GetValueOrDefault() == flag2 & flag.HasValue) 252 { 253 ValidationUtility.EnableDynamicValidation(current); 254 } 255 } 256 this.AddVersionHeader(httpContext); 257 this.RemoveOptionalRoutingParameters(); 258 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); 259 factory = this.ControllerBuilder.GetControllerFactory(); 260 controller = factory.CreateController(this.RequestContext, requiredString); 261 if (controller == null) 262 { 263 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] 264 { 265 factory.GetType(), 266 requiredString 267 })); 268 } 269 } 270 271 private void RemoveOptionalRoutingParameters() 272 { 273 IDictionary<string, object> arg_2F_0 = this.RequestContext.RouteData.Values; 274 Func<KeyValuePair<string, object>, bool> arg_2F_1; 275 if ((arg_2F_1 = MvcHandler.<>c.<>9__26_0) == null) 276 { 277 arg_2F_1 = (MvcHandler.<>c.<>9__26_0 = new Func<KeyValuePair<string, object>, bool>(MvcHandler.<>c.<>9.<RemoveOptionalRoutingParameters>b__26_0)); 278 } 279 arg_2F_0.RemoveFromDictionary(arg_2F_1); 280 } 281 282 /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary> 283 /// <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> 284 void IHttpHandler.ProcessRequest(HttpContext httpContext) 285 { 286 this.ProcessRequest(httpContext); 287 } 288 289 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary> 290 /// <returns>The status of the asynchronous call.</returns> 291 /// <param name="context">The HTTP context.</param> 292 /// <param name="cb">The asynchronous callback method.</param> 293 /// <param name="extraData">The data.</param> 294 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) 295 { 296 return this.BeginProcessRequest(context, cb, extraData); 297 } 298 299 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary> 300 /// <param name="result">The asynchronous result.</param> 301 void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) 302 { 303 this.EndProcessRequest(result); 304 } 305 } 306 }
View Code
HttpRuntime 調用 IHttpHandler 類型的調用 ProcessRequest() 方法,用於處理請求。
1 protected internal virtual void ProcessRequest(HttpContextBase httpContext) 2 { 3 IController controller; 4 IControllerFactory controllerFactory; 5 this.ProcessRequestInit(httpContext, out controller, out controllerFactory);創建 IControllerFactory,並創建 IController 對象。 6 try 7 { 8 controller.Execute(this.RequestContext);執行Controller,背後就是調用相應的 Action 方法。 9 } 10 finally 11 { 12 controllerFactory.ReleaseController(controller); 13 } 14 }
核心處理請求的方法是ProcessRequestInit(),用於創建 IController 和 IControllerFactory 實例。IControllerFactory 的實際類型是:DefaultControllerFactory,該類型用於創建 IController 類型的實例。
1 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) 2 { 3 HttpContext current = HttpContext.Current; 4 if (current != null) 5 { 6 bool? flag = ValidationUtility.IsValidationEnabled(current); 7 bool flag2 = true; 8 if (flag.GetValueOrDefault() == flag2 & flag.HasValue) 9 { 10 ValidationUtility.EnableDynamicValidation(current); 11 } 12 } 13 this.AddVersionHeader(httpContext); 14 this.RemoveOptionalRoutingParameters(); 15 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); 16 factory = this.ControllerBuilder.GetControllerFactory(); 17 controller = factory.CreateController(this.RequestContext, requiredString); 18 if (controller == null) 19 { 20 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] 21 { 22 factory.GetType(), 23 requiredString 24 })); 25 } 26 }
以上加紅的程式碼就是創建 IController 的實例的邏輯。IController 實例創建完成後,判斷是否實現了 IAsyncController 介面,如果是,就非同步執行 Controller 方法的調用,否則就同步執行。
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> arg_51_0; 10 if ((arg_51_0 = MvcHandler.<>c.<>9__20_0) == null) 11 { 12 arg_51_0 = (MvcHandler.<>c.<>9__20_0 = new BeginInvokeDelegate<MvcHandler.ProcessRequestState>(MvcHandler.<>c.<>9.<BeginProcessRequest>b__20_0)); 13 } 14 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = arg_51_0; 15 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> arg_71_0; 16 if ((arg_71_0 = MvcHandler.<>c.<>9__20_1) == null) 17 { 18 arg_71_0 = (MvcHandler.<>c.<>9__20_1 = new EndInvokeVoidDelegate<MvcHandler.ProcessRequestState>(MvcHandler.<>c.<>9.<BeginProcessRequest>b__20_1)); 19 } 20 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = arg_71_0; 21 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState 22 { 23 AsyncController = asyncController, 24 Factory = factory, 25 RequestContext = this.RequestContext 26 }; 27 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext(); 28 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext); 29 } 30 Action action = delegate//同步執行。 31 { 32 try 33 { 34 controller.Execute(this.RequestContext); 35 } 36 finally 37 { 38 factory.ReleaseController(controller); 39 } 40 }; 41 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag); 42 }
(3)、Action 執行模組,通過 ControllerActionInvoker 調用 InvokeAction() 執行其方法。Action 方法的執行也有2個版本,一個是非同步版本,一個是同步版本。由於 ActionInvoker 實現了 IAsyncActionInvoker 介面,所以也是以已方式執行。該類型是 AsyncControllerActionInvoker。
A、當Controller對象被創建之後,緊接著就會執行Controler 對象的 Execute(),其實背後就是調用 InvokeAction() 方法:
1 public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) 2 { 3 if (controllerContext == null) 4 { 5 throw new ArgumentNullException("controllerContext"); 6 } 7 if (string.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch()) 8 { 9 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName"); 10 } 11 ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext); 12 ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName); 13 if (actionDescriptor != null) 14 { 15 FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor); 獲取所有過濾器,全局的、控制器的和方法的 16 try 17 { 18 AuthenticationContext authenticationContext = this.InvokeAuthenticationFilters(controllerContext, filters.AuthenticationFilters, actionDescriptor);認證過濾器的執行。 19 if (authenticationContext.Result != null) 20 { 21 AuthenticationChallengeContext authenticationChallengeContext = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authenticationContext.Result); 22 this.InvokeActionResult(controllerContext, authenticationChallengeContext.Result ?? authenticationContext.Result); 23 } 24 else 25 { 26 AuthorizationContext authorizationContext = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);授權過濾器的執行。 27 if (authorizationContext.Result != null) 28 { 29 AuthenticationChallengeContext authenticationChallengeContext2 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authorizationContext.Result); 30 this.InvokeActionResult(controllerContext, authenticationChallengeContext2.Result ?? authorizationContext.Result); 31 } 32 else 33 { 34 if (controllerContext.Controller.ValidateRequest) 35 { 36 ControllerActionInvoker.ValidateRequest(controllerContext); 37 } 38 IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor); 獲取方法執行參數。 39 ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues); 執行action,同時執行執行方法前後的 IAcctionFilter 40 AuthenticationChallengeContext authenticationChallengeContext3 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, actionExecutedContext.Result); 41 this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, authenticationChallengeContext3.Result ?? actionExecutedContext.Result); 執行 ActionResult,同時執行方法前後的 IResultFilter 42 } 43 } 44 } 45 catch (ThreadAbortException) 46 { 47 throw; 48 } 49 catch (Exception exception) 50 { 51 ExceptionContext exceptionContext = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception); 52 if (!exceptionContext.ExceptionHandled) 53 { 54 throw; 55 } 56 this.InvokeActionResult(controllerContext, exceptionContext.Result);//異常過濾器的執行。 57 } 58 return true; 59 } 60 return false; 61 }
B、當選擇完合適的Action後,接著就是 ModelBinder(默認是System.Web.Mvc.DefaultModelBinder),它會從http請求的參數中提取數據並實現類型轉換,數據校驗(例如是否必填,數據格式等)以及是否自動裝配到action方法的參數中System.Web.Mvc.DefaultModelBinder
1 protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 2 { 3 Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 4 ParameterDescriptor[] parameters = actionDescriptor.GetParameters(); 5 for (int i = 0; i < parameters.Length; i++) 6 { 7 ParameterDescriptor parameterDescriptor = parameters[i]; 8 dictionary[parameterDescriptor.ParameterName] = this.GetParameterValue(controllerContext, parameterDescriptor); 9 } 10 return dictionary; 11 }
1 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) 2 { 3 Type parameterType = parameterDescriptor.ParameterType; 4 IModelBinder arg_92_0 = this.GetModelBinder(parameterDescriptor); 5 IValueProvider valueProvider = controllerContext.Controller.ValueProvider; 6 string modelName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName; 7 Predicate<string> propertyFilter = ControllerActionInvoker.GetPropertyFilter(parameterDescriptor); 8 ModelBindingContext bindingContext = new ModelBindingContext 9 { 10 FallbackToEmptyPrefix = parameterDescriptor.BindingInfo.Prefix == null, 11 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType), 12 ModelName = modelName, 13 ModelState = controllerContext.Controller.ViewData.ModelState, 14 PropertyFilter = propertyFilter, 15 ValueProvider = valueProvider 16 }; 17 return arg_92_0.BindModel(controllerContext, bindingContext) ?? parameterDescriptor.DefaultValue; 18 }
C、Authentication Filter是mvc5中新增的一個Filter,它會先於authorization filter執行,目的是對訪問用戶的認證。在MVC5之前,認證和授權都是通過authorization filter來實現的,但現在這2個操作就分開來了,各自管各自嘍。
1 AuthenticationContext authenticationContext = this.InvokeAuthenticationFilters(controllerContext, filters.AuthenticationFilters, actionDescriptor); 2 if (authenticationContext.Result != null) 3 { 4 AuthenticationChallengeContext authenticationChallengeContext = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authenticationContext.Result); 5 this.InvokeActionResult(controllerContext, authenticationChallengeContext.Result ?? authenticationContext.Result); 6 }
D、Action filters有2個方法OnActionExecuting和OnActionExecuted分別在action執行前後執行。我們也可以通過實現IActionFilter介面來實現你個性化的過濾機制
1 protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) 2 { 3 ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters); 4 Func<ActionExecutedContext> seed = () => new ActionExecutedContext(controllerContext, actionDescriptor, false, null) 5 { 6 Result = this.InvokeActionMethod(controllerContext, actionDescriptor, parameters) 7 }; 8 return filters.Reverse<IActionFilter>().Aggregate(seed, (Func<ActionExecutedContext> next, IActionFilter filter) => () => ControllerActionInvoker.InvokeActionMethodFilter(filter, preContext, next))(); 9 }
E、接下來就是執行我們平時在Action方法中寫的程式碼了(根據請求相應結果)
1 protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) 2 { 3 object actionReturnValue = actionDescriptor.Execute(controllerContext, parameters); 4 return this.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue); 5 }
(4)、ActionResult 執行模組。
A、在 ActionResult 執行前後,仍然會有一個filter(IResultFilter),同樣的,通過實現 IResultFilter 介面你可以訂製自己的過濾邏輯。
1 namespace System.Web.Mvc 2 { 3 /// <summary>Defines the methods that are required for a result filter.</summary> 4 public interface IResultFilter 5 { 6 /// <summary>Called before an action result executes.</summary> 7 /// <param name="filterContext">The filter context.</param> 8 void OnResultExecuting(ResultExecutingContext filterContext); 9 10 /// <summary>Called after an action result executes.</summary> 11 /// <param name="filterContext">The filter context.</param> 12 void OnResultExecuted(ResultExecutedContext filterContext); 13 } 14 }
B、ActionResult 就是把處理的用戶請求結果返回。因此 ViewResult, PartialViewResult, RedirectToRouteResult, RedirectResult, ContentResult, JsonResult, FileResult and EmptyResult就是具體的返回類型。
C、上面的返回類型可以大致分為2類:ViewResult 和非ViewResult。對於需要生成html頁面給客戶端的划到ViewResult,而其他的例如返迴文本,json數據等則劃分到非ViewResult,對於非ViewResult直接返回就可以了。
View的初始化和渲染呈現
A、對於 ViewResult 最終是由合適的 View Engine 通過調用 IView 的 Render() 方法來渲染的:
1 namespace System.Web.Mvc 2 { 3 /// <summary>Defines the methods that are required for a view engine.</summary> 4 public interface IViewEngine 5 { 6 /// <summary>Finds the specified partial view by using the specified controller context.</summary> 7 /// <returns>The partial view.</returns> 8 /// <param name="controllerContext">The controller context.</param> 9 /// <param name="partialViewName">The name of the partial view.</param> 10 /// <param name="useCache">true to specify that the view engine returns the cached view, if a cached view exists; otherwise, false.</param> 11 ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache); 12 13 /// <summary>Finds the specified view by using the specified controller context.</summary> 14 /// <returns>The page view.</returns> 15 /// <param name="controllerContext">The controller context.</param> 16 /// <param name="viewName">The name of the view.</param> 17 /// <param name="masterName">The name of the master.</param> 18 /// <param name="useCache">true to specify that the view engine returns the cached view, if a cached view exists; otherwise, false.</param> 19 ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache); 20 21 /// <summary>Releases the specified view by using the specified controller context.</summary> 22 /// <param name="controllerContext">The controller context.</param> 23 /// <param name="view">The view.</param> 24 void ReleaseView(ControllerContext controllerContext, IView view); 25 } 26 }
1 namespace System.Web.Mvc 2 { 3 /// <summary>Defines the methods that are required for a view.</summary> 4 public interface IView 5 { 6 /// <summary>Renders the specified view context by using the specified the writer object.</summary> 7 /// <param name="viewContext">The view context.</param> 8 /// <param name="writer">The writer object.</param> 9 void Render(ViewContext viewContext, TextWriter writer); 10 } 11 }
B、整個處理過程是由 IViewEngine 來實現的。ASP.NET MVC 默認提供 WebForm(.aspx)和 Razor(.cshtml) 模板引擎,你可以通過實現 IViewEngine 介面來實現自己的 ViewEngine,然後在Application_Start方法中做如下註冊:
protected void Application_Start() { //移除所有的View引擎包括Webform和Razor ViewEngines.Engines.Clear(); //註冊你自己的View引擎 ViewEngines.Engines.Add(new CustomViewEngine()); }
C、最後,Html Helpers將幫我們生成 input 標籤,基於AJAX的 form 等等。
(5)、作為總結,將每個節點主要的程式碼類貼出來。
這就是整個流程的程式碼節點,有些是同步執行,有些是非同步執行,把握關鍵點,我這裡只是謝了一個大概。
UrlRoutingModule—–RouteCollection.GetRouteData(context)—–>IRouteHandler routeHandler = routeData.RouteHandler——》IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext)—–》context.RemapHandler(httpHandler)——->MvcHandler——->ProcessRequest()——>ProcessRequestInit()——–》IController——>controller.Execute(this.RequestContext)——–>ControllerActionInvoker——->InvoleAction()———>InvoleActionMethod()——->InvoleActionReslt()
三、結束
今天就到這裡了,東西雖然不多,但是也寫了2個多小時。今天就算自己有學習了一邊,大家一定要好好的把握這個流程,對於解決程式中的問題,擴展框架都有很大的好處。我們作為程式設計師的,應該要知道其一,也要知道其二。沒事,看看源碼,我們對框架和我們自己的程式碼有更深的了解。當然,這樣做也是有代價的,需要更多的時間去支援,我相信我們的付出是值得。不忘初心,繼續努力。老天不會辜負努力的人。