首先咱們知道http是一種無狀態的請求,他的生命週期就是從客戶端瀏覽器發出請求開始,到獲得響應結束。那麼MVC應用程序從發出請求到得到響應,都作了些什麼呢?html
本文咱們會詳細討論MVC應用程序一個請求的生命週期,從一個控件到另外一個控件是怎樣被處理的。咱們還會詳細介紹一下整個請求的生命週期中,用到的相關組件。由於在日常的開發過程當中,咱們可能知道怎樣去使用MVC框架來處理相關的請求,大部分的時候咱們只是在controller和action方法之間作相關的處理,對於真正內在的運行機制可能不是很瞭解。其實當咱們對內在機制有了必定的瞭解之後,會發現微軟的MVC框架的擴展性很強,處處都留有擴展接口,讓咱們經過擴展可以本身定義本身所須要的處理機制,這也正是爲何MVC框架如此出名的緣由。web
當我最開始學習使用mvc的時候,困擾個人一個問題就是,一個請求的流程控制是怎樣的呢?從view到controller再到action之間經歷了什麼?那個時候我還不清楚HTTP module和HTTP handler在處理一個請求中扮演什麼樣的角色,起什麼樣的做用呢。畢竟MVC是一個web開發框架,在整個請求處理過程當中,確定包含了http module和http handler。其實還有不少相關的組件包含在一個完整的mvc應用程序請求生命週期裏,在整個請求過程當中他們都扮演者很是重要的角色。儘管大部分時候咱們都使用的是框架提供的默認的函數,可是若是咱們瞭解了每一個控件所扮演的角色,咱們就能夠輕鬆的擴展和使用咱們本身實現的方法,就目前來講MVC是擴展性比較強的框架。下面是本章節的主要內容:瀏覽器
咱們都知道,在ASP.NET MVC框架出現以前,咱們大部分開發所使用的框架都是ASP.NET WebForm.其實不論是MVC仍是WebForm,在請求處理機制上,大部分是相同的。這涉及到IIS對請求的處理,涉及的知識較多,咱們就不作介紹了,下次有機會我寫一篇專文。咱們從HttpApplication提及。先看看微軟官方是怎麼定義HttpApplication的:緩存
定義 ASP.NET 應用程序中的全部應用程序對象共有的方法、屬性和事件。此類是用戶在 Global.asax 文件中所定義的應用程序的基類。安全
可能我翻譯不是很準確,原文鏈接在這裏:https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.110).aspx架構
微軟官方文檔中Remark裏有這麼一段話:HttpApplication 類的實例是在 ASP.NET 基礎結構中建立的,而不是由用戶直接建立的。使用 HttpApplication 類的一個實例來處理其生存期中收到的衆多請求。可是,它每次只能處理一個請求。這樣,成員變量纔可用於存儲針對每一個請求的數據。mvc
意思就是說ASP.NET應用程序,不論是MVC仍是WebForm,最終都會到達一個HttpApplication類的實例。HttpApplication是整個ASP.NET基礎架構的核心,負責處理分發給他的請求。HttpApplication處理請求的週期是一個複雜的過程,在整個過程當中,不一樣階段會觸發相映的事件。咱們能夠註冊相應的事件,將處理邏輯注入到HttpApplication處理請求的某個階段。在HttpApplication這個類中定義了19個事件來處理到達HttpApplication實例的請求。就是說無論MVC仍是WebForm,最終都要通過這19個事件的處理,那麼除了剛纔說的MVC和WebFrom在請求處理機制上大部分都是相同的,不一樣之處在哪呢?他們是從哪裏開始分道揚鑣的呢?咱們猜測確定就在這19個方法中。咱們繼續往下看。app
咱們來看看這19個事件:框架
應用程序按照如下順序執行由 global.asax 文件中定義的模塊或用戶代碼處理的事件:ide
事件名稱: |
簡單描述: |
BeginRequest |
在 ASP.NET 響應請求時做爲 HTTP 執行管線鏈中的第一個事件發生 |
AuthenticateRequest |
當安全模塊已創建用戶標識時發生。注:AuthenticateRequest 事件發出信號表示配置的身份驗證機制已對當前請求進行了身份驗證。預訂 AuthenticateRequest 事件可確保在處理附加的模塊或事件處理程序以前對請求進行身份驗證
|
PostAuthenticateRequest |
當安全模塊已創建用戶標識時發生。PostAuthenticateRequest 事件在 AuthenticateRequest 事件發生以後引起。預訂 PostAuthenticateRequest 事件的功能能夠訪問由 PostAuthenticateRequest 處理的任何數據 |
AuthorizeRequest |
當安全模塊已驗證用戶受權時發生。AuthorizeRequest 事件發出信號表示 ASP.NET 已對當前請求進行了受權。預訂 AuthorizeRequest 事件可確保在處理附加的模塊或事件處理程序以前對請求進行身份驗證和受權 |
PostAuthorizeRequest |
在當前請求的用戶已獲受權時發生。PostAuthorizeRequest 事件發出信號表示 ASP.NET 已對當前請求進行了受權。預訂PostAuthorizeRequest 事件可確保在處理附加的模塊或處理程序以前對請求進行身份驗證和受權 |
ResolveRequestCache |
當 ASP.NET 完成受權事件以使緩存模塊從緩存中爲請求提供服務時發生,從而跳過事件處理程序(例如某個頁或 XML Web services)的執行 |
PostResolveRequestCache |
在 ASP.NET 跳過當前事件處理程序的執行並容許緩存模塊知足來自緩存的請求時發生。)在 PostResolveRequestCache 事件以後、PostMapRequestHandler 事件以前建立一個事件處理程序(對應於請求 URL 的頁 |
PostMapRequestHandler |
在 ASP.NET 已將當前請求映射到相應的事件處理程序時發生。
|
AcquireRequestState |
當 ASP.NET 獲取與當前請求關聯的當前狀態(如會話狀態)時發生。
|
PostAcquireRequestState |
在已得到與當前請求關聯的請求狀態(例如會話狀態)時發生。
|
PreRequestHandlerExecute |
剛好在 ASP.NET 開始執行事件處理程序(例如,某頁或某個 XML Web services)前發生。
|
PostRequestHandlerExecute |
在 ASP.NET 事件處理程序(例如,某頁或某個 XML Web service)執行完畢時發生。
|
ReleaseRequestState
|
在 ASP.NET 執行完全部請求事件處理程序後發生。該事件將使狀態模塊保存當前狀態數據。
|
PostReleaseRequestState
|
在 ASP.NET 已完成全部請求事件處理程序的執行而且請求狀態數據已存儲時發生。
|
UpdateRequestCache
|
當 ASP.NET 執行完事件處理程序以使緩存模塊存儲將用於從緩存爲後續請求提供服務的響應時發生。
|
PostUpdateRequestCache
|
在 ASP.NET 完成緩存模塊的更新並存儲了用於從緩存中爲後續請求提供服務的響應後,發生此事件。
|
LogRequest
|
在 ASP.NET 完成緩存模塊的更新並存儲了用於從緩存中爲後續請求提供服務的響應後,發生此事件。 僅在 IIS 7.0 處於集成模式而且 .NET Framework 至少爲 3.0 版本的狀況下才支持此事件
|
PostLogRequest
|
在 ASP.NET 處理完 LogRequest 事件的全部事件處理程序後發生。 僅在 IIS 7.0 處於集成模式而且 .NET Framework 至少爲 3.0 版本的狀況下才支持此事件。
|
EndRequest
|
在 ASP.NET 響應請求時做爲 HTTP 執行管線鏈中的最後一個事件發生。 在調用 CompleteRequest 方法時始終引起 EndRequest 事件。
|
對於一個ASP.NET應用程序來講,HttpApplication派生與Global.aspx(能夠看看咱們建立的應用程序都有一個Global.aspx文件),咱們能夠在Global.aspx文件中對HttpApplication的請求進行定製即注入這19個事件中的某個事件進行邏輯處理操做。在Global.aspx中咱們按照"Application_{Event Name}"這樣的方法命名進行事件註冊。
Event Name就是上面19個事件的名稱。好比Application_EndRequest就用於處理Application的EndRequest事件。
ASP.NET擁有一個高度可擴展的引擎,而且可以處理對於不一樣資源類型的請求。這就是HttpModule。當一個請求轉入ASP.net管道時,最終負責處理請求的是與資源相匹配的HttpHandler對象,可是在HttpHandler進行處理以前,ASP.NET先會加載並初始化全部配置的HttpModule對象。HttpModule初始化的時候,會將一些回調事件注入到HttpApplication相應的事件中。全部的HttpModule都實現了IHttpModule接口,該接口有一個有一個Init方法。
public interface IHttpModule { // Methods void Dispose(); void Init(HttpApplication context); }
看到Init方法呢接受一個HttpApplication對象,有了這個對象就很容易註冊HttpApplication中19個事件中的某個事件了。這樣當HttpApplication對象執行到某個事件的時候天然就會出發。
對於不一樣的資源類型的請求,ASP.NET會加載不一樣的HttpHandler來處理。全部的HttpHandler都實現了IhttpHandler接口。
public interface IHttpHandler { // Methods void ProcessRequest(HttpContext context); // Properties bool IsReusable { get; } }
咱們看到該接口有一個方法ProcessRequest,顧名思義這個方法就是主要用來處理請求的。因此說每個請求最終分發到本身相應的HttpHandler來處理該請求。
好了,上面說了那麼多,其實都是給這裏作鋪墊呢。終於到正題了。先看看下面這張圖,描述了MVC的主要經歷的管道事件:
上圖就是一個完整的mvc應用程序的一個http請求到響應的整個兒所經歷的流程。從UrlRoutingModule攔截請求到最終ActionResult執行ExecuteResult方法生成響應。
下面咱們就來詳細講解一下這些過程都作了些什麼。
MVC應用程序的入口UrlRoutingModule
首先發起一個請求,咱們前面講到ASP.NET 會加載一個HttpModule對象的初始化事件Init,而全部的HttpModule對象都實現了IHttpModule接口。咱們看看UrlRoutingModule的實現:
從上圖中咱們看到UrlRoutingModule實現了接口IHttpModule,當一個請求轉入ASP.NET管道時,就會加載 UrlRoutingModule對象的Init()方法。
那麼爲何恰恰是UrlRoutingModule被加載初始化了呢?爲何不是別的HttpModule對象呢?帶着這個疑問咱們繼續。
在ASP.NET MVC中,最核心的當屬「路由系統」,而路由系統的核心則源於一個強大的System.Web.Routing.dll組件。System.Web.Routing.dll 不是MVC所特有的,可是MVC框架和它是密不可分的。
首先,咱們要了解一下UrlRoutingModule是如何起做用的。
(1)IIS網站的配置能夠分爲兩個塊:全局 Web.config 和本站 Web.config。Asp.Net Routing屬於全局性的,因此它配置在全局Web.Config 中,咱們能夠在以下路徑中找到:「C\Windows\Microsoft.NET\Framework\版本號\Config\Web.config「,我提取部分重要配置你們看一下:
<httpModules> <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" /> <add name="Session" type="System.Web.SessionState.SessionStateModule" /> <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" /> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" /> <add name="RoleManager" type="System.Web.Security.RoleManagerModule" /> <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" /> <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" /> <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" /> <add name="Profile" type="System.Web.Profile.ProfileModule" /> <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" /> <add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </httpModules>
你們看到沒有,我上面標紅的那一行:<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
UrlRoutingModule並非MVC特有的,這是一個全局配置,就是說全部的ASP.NET請求都會到達這裏,因此該Module還不能最終決定是MVC仍是WebForm請求。可是也是相當重要的地方。
(2)經過在全局Web.Config中註冊 System.Web.Routing.UrlRoutingModule,IIS請求處理管道接到請求後,就會加載 UrlRoutingModule類型的Init()方法。其源碼入下:
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] public class UrlRoutingModule : IHttpModule { // Fields private static readonly object _contextKey = new object(); private static readonly object _requestDataKey = new object(); private RouteCollection _routeCollection; // Methods protected virtual void Dispose() { } protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] == null) { application.Context.Items[_contextKey] = _contextKey; application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); } } private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpApplication application = (HttpApplication) sender; HttpContextBase context = new HttpContextWrapper(application.Context); this.PostResolveRequestCache(context); } [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")] public virtual void PostMapRequestHandler(HttpContextBase context) { } public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this.RouteCollection.GetRouteData(context); if (routeData != null) { IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (httpHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() })); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) { throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3")); } UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); } else { context.RemapHandler(httpHandler); } } } } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] void IHttpModule.Dispose() { this.Dispose(); } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] void IHttpModule.Init(HttpApplication application) { this.Init(application); } // Properties public RouteCollection RouteCollection { get { if (this._routeCollection == null) { this._routeCollection = RouteTable.Routes; } return this._routeCollection; } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set { this._routeCollection = value; } } }
看看上面的UrlRoutingModule源碼裏面是怎麼實現Init方法的,Init()方法裏面我標註紅色的地方:
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
這一步相當重要哈,看到沒有,就是對咱們在HttpApplication那19個事件中的PostResolveRequestCache事件的註冊。註冊的方法是OnApplicationPostResolveRequestCache事件。也就是說HttpApplication對象在執行到PostResolveRequestCache這個事件的時候,就會執行OnApplicationPostResolveRequestCache事件。決定是MVC機制處理請求的關鍵所在就是OnApplicationPostResolveRequestCache事件。
從源碼中咱們看出,OnApplicationPostResolveRequestCache事件執行的時候,最終執行了PostResolveRequestCache這個方法。最關鍵的地方呢就在這裏了。
當請求到達UrlRoutingModule的時候,UrlRoutingModule取出請求中的Controller、Action等RouteData信息,與路由表中的全部規則進行匹配,若匹配,把請求交給IRouteHandler,即MVCRouteHandler。咱們能夠看下UrlRoutingModule的源碼來看看,如下是幾句核心的代碼:
咱們再分析一下這個方法的源碼:
1 public virtual void PostResolveRequestCache(HttpContextBase context) 2 { 3 // 經過RouteCollection的靜態方法GetRouteData獲取到封裝路由信息的RouteData實例 4 RouteData routeData = this.RouteCollection.GetRouteData(context); 5 if (routeData != null) 6 { 7 // 再從RouteData中獲取MVCRouteHandler 8 IRouteHandler routeHandler = routeData.RouteHandler; 9 ...... 10 if (!(routeHandler is StopRoutingHandler)) 11 { 12 ...... 13 // 調用 IRouteHandler.GetHttpHandler(),獲取的IHttpHandler 類型實例,它是由 IRouteHandler.GetHttpHandler獲取的,這個得去MVC的源碼裏看 14 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 15 ...... 16 // 合適條件下,把以前將獲取的IHttpHandler 類型實例 映射到IIS HTTP處理管道中 17 context.RemapHandler(httpHandler); 18 } 19 } 20 }
看到了吧,經過路由規則,返回的不爲空,說明匹配正確,關於路由規則的匹配,提及來也不短,這裏就不大幅介紹,有時間下次再開篇詳解路由機制。匹配成功後,返回一個RouteData類型的對象,RouteData對象都有些什麼屬性呢?看看這行源碼: IRouteHandler routeHandler = routeData.RouteHandler;或者看源碼咱們知道,RouteDate有一個RouteHandler屬性。
那麼UrlRouting Module是如何選擇匹配規則的呢?
咱們看看咱們新建的MVC應用程序,在App_Start文件夾下面有一個RouteConfig.cs類,這個類的內容以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using System.Web.Routing; 7 8 namespace ApiDemo 9 { 10 public class RouteConfig 11 { 12 public static void RegisterRoutes(RouteCollection routes) 13 { 14 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 16 routes.MapRoute( 17 name: "Default", 18 url: "{controller}/{action}/{id}", 19 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 ); 21 } 22 } 23 }
咱們在這個類裏面,主要是給路由表添加路由規則。在看看上面的UrlRoutingModule類,裏面有一個RoutCollection屬性,因此UrlRoutingModule可以獲取路由表中的全部規則,這裏值得注意的是,路由規則的匹配是有順序的,若是有多個規則都可以匹配,UrlRoutingModule至選擇第一個匹配的規則就返回,再也不繼續往下匹配了。相反的若是一個請求,沒有匹配到任何路由,那麼該請求就不會被處理。
這裏返回的RouteData裏的RouteHandler就是MVCRouteHandler。爲何呢?那咱們繼續往下看RouteHandler。
生成MvcHander
在上面路由匹配的過程當中,與匹配路由相關聯的MvcRouteHandler ,MvcRouteHandler 實現了IRouteHandler 接口。MvcRouteHandler 主要是用來獲取對MvcHandler的引用。MvcHandler實現了IhttpHandler接口。
MVCRouteHandler的做用是用來生成實現IHttpHandler接口的MvcHandler。而咱們前面說過最終處理請求的都是相對應的HttpHandler。那麼處理MVC請求的天然就是這個MvcHandler。因此這裏返回MvcRouteHandler相當重要:
那麼,MvcRouteHandler從何而來呢?衆所周知,ASP.NET MVC項目啓動是從Global中的Application_Start()方法開始的,那就去看看它:
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); System.Web.Mvc.RouteCollectionExtensions routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
看看我上面標紅的代碼:這是路由註冊,玄機就在這裏。那咱們去看看MapRoute源碼就知道咯:
1 public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 2 { 3 ...... 4 5 Route route = new Route(url, new MvcRouteHandler()) { 6 Defaults = new RouteValueDictionary(defaults), 7 Constraints = new RouteValueDictionary(constraints), 8 DataTokens = new RouteValueDictionary() 9 }; 10 ...... 11 return route; 12 }
看看咱們5-8行代碼,在MVC應用程序裏,在路由註冊的時候,咱們就已經給他一個默認的HttpRouteHandler對象,就是 New MvcRouteHandler().如今咱們反推回去,咱們MVC程序在路由註冊的時候就已經肯定了HttpRouteHandler爲MvcRouteHandler,那麼當咱們在前面PostResolveRequestCache方法裏,當咱們的請求與路由匹配成功後,天然會返回的是MvcRouteHandler。
好啦,MvcRouteHandler生成了。那麼MvcRouteHandler能作什麼呢?又作了什麼呢?
再回頭看看 PostResolveRequestCache方法,在成功獲取到IHttpRouteHandler對象即MvcRouteHandler以後,又作了下面這一個操做:
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
咱們看看這個IHttpHandler 的源碼:
namespace System.Web.Routing { public interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); } }
有一個GetHttpHandler的方法,剛好就調用了這個方法。那咱們看看MvcRouteHandler是怎麼實現這個GetHttpHandler的呢:
1 public class MvcRouteHandler : IRouteHandler 2 { 3 // Fields 4 private IControllerFactory _controllerFactory; 5 6 // Methods 7 public MvcRouteHandler() 8 { 9 } 10 11 public MvcRouteHandler(IControllerFactory controllerFactory) 12 { 13 this._controllerFactory = controllerFactory; 14 } 15 16 protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) 17 { 18 requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext)); 19 return new MvcHandler(requestContext); 20 } 21 22 protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) 23 { 24 string str = (string) requestContext.RouteData.Values["controller"]; 25 if (string.IsNullOrWhiteSpace(str)) 26 { 27 throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController); 28 } 29 IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); 30 return factory.GetControllerSessionBehavior(requestContext, str); 31 } 32 33 IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) 34 { 35 return this.GetHttpHandler(requestContext); 36 } 37 } 38 39
看第16-20行代碼,這時候應該明白了吧。瓜熟蒂落的返回了MvcHandler對象。記得咱們前面說過,請求最終是被相對應的HttpHander對象處理的。MvcHandler就是那個用來處理Mvc請求的HttpHandler。MvcRouteHandler把請求交給了MvcHandler去作請求處理管道中後續事件的處理操做了。
下面咱們就看看MvcHandler作了些什麼:
MvcHandler就是最終對request進行處理。
MvcHandler的定義以下:
咱們能夠看到MvcHandler就是一個普通的Http Handler.咱們知道一個http handler須要實現一個ProcessRequest()的方法,這個方法就是處理request的核心。因此MvcHandler實現了ProcessRequest()方法。
ProcessRequest主要功能:
(1)在ASP.NET MVC中,會調用MvcHandler的ProcessRequest()方法,此方法會激活具體請求的Controller類對象,觸發Action方法,返回ActionResult實例。
(2)若是ActionResult是非ViewResult,好比JsonResult, ContentResult,這些內容將直接被輸送到Response響應流中,顯示給客戶端;若是是ViewResult,就會進入下一個渲染視圖環節。
(3)在渲染視圖環節,ViewEngine找到須要被渲染的視圖,View被加載成WebViewPage<TModel>類型,並渲染生成Html,最終返回Html。
ProcessRequest()定義以下:
1 // Copyright (c) Microsoft Open Technologies, Inc.<pre>// All rights reserved. See License.txt in the project root for license information. 2 void IHttpHandler.ProcessRequest(HttpContext httpContext) 3 { 4 ProcessRequest(httpContext); 5 } 6 protected virtual void ProcessRequest(HttpContext httpContext) 7 { 8 HttpContextBase iHttpContext = new HttpContextWrapper(httpContext); 9 ProcessRequest(iHttpContext); 10 } 11 protected internal virtual void ProcessRequest(HttpContextBase httpContext) { 12 SecurityUtil.ProcessInApplicationTrust(() => { 13 IController controller; 14 IControllerFactory factory; 15 ProcessRequestInit(httpContext, out controller, out factory); 16 try 17 { 18 controller.Execute(RequestContext); 19 } 20 finally 21 { 22 factory.ReleaseController(controller); 23 } 24 }); 25 }
從上面的代碼能夠看出調用了一個ProcessRequestInit()方法,定義以下:
1 private void ProcessRequestInit(HttpContextBase httpContext, 2 out IController controller, out IControllerFactory factory) { 3 // If request validation has already been enabled, make it lazy. 4 // This allows attributes like [HttpPost] (which looks 5 // at Request.Form) to work correctly without triggering full validation. 6 bool? isRequestValidationEnabled = 7 ValidationUtility.IsValidationEnabled(HttpContext.Current); 8 if (isRequestValidationEnabled == true) { 9 ValidationUtility.EnableDynamicValidation(HttpContext.Current); 10 } 11 AddVersionHeader(httpContext); 12 RemoveOptionalRoutingParameters(); 13 // Get the controller type 14 string controllerName = RequestContext.RouteData.GetRequiredString("controller"); 15 // Instantiate the controller and call Execute 16 factory = ControllerBuilder.GetControllerFactory(); 17 controller = factory.CreateController(RequestContext, controllerName); 18 if (controller == null) { 19 throw new InvalidOperationException( 20 String.Format( 21 CultureInfo.CurrentCulture, 22 MvcResources.ControllerBuilder_FactoryReturnedNull, 23 factory.GetType(), 24 controllerName)); 25 } 26 }
在ProcessRequestInit()方法中首先建立了ControllerFactory()的對象 factory.而後ControllerFactory建立了相關Controller的實例.最終調用了Controller的Excute()方法。
好咱們再來看看ControllerFactory:
主要是用來生成Controller對象
ControllerFactory實現了接口IControllerFactory.
到這裏咱們大概就知道了,MvcHandler經過ProcessRequest()方法最終建立了Controller對象,這裏咱們都應該知道,Controller裏面包含不少的Action方法,每一次請求至少一個Action方法會被調用。爲了明確的實現IController接口,框架裏面有一個ControllerBase的類已經實現了IController接口,其實咱們本身的Controller也能夠不繼承ControllerBase,只要實現IController接口便可。
1 public abstract class ControllerBase : IController 2 { 3 protected virtual void Execute(RequestContext requestContext) 4 { 5 if (requestContext == null) 6 { 7 throw new ArgumentNullException("requestContext"); 8 } 9 if (requestContext.HttpContext == null) 10 { 11 throw new ArgumentException( 12 MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, 13 "requestContext"); 14 } 15 VerifyExecuteCalledOnce(); 16 Initialize(requestContext); 17 using (ScopeStorage.CreateTransientScope()) 18 { 19 ExecuteCore(); 20 } 21 } 22 protected abstract void ExecuteCore(); 23 // .......
controller對象實際上使用ActionInvoker來調用Action方法的,當Controller對象被建立後,會執行Controller對象的基類ControllerBase類裏面的Excute方法。Excute方法又調用了ExcuteCore()方法。Controller類裏面實現了ExcuteCore()方法。ExcuteCore調用了ActionInvoker的InvokerAction方法來調用Action方法。
ActionInvoker方法有很重要的責任來查找Controller中的Action方法而且調用。
ActionInvoker是一個實現了IActionInvoker接口的對象:
bool InvokeAction( ControllerContext controllerContext, string actionName )
Controller類裏面暴露了一個ActionInvoker 屬性,會返回一個ControllerActionInvoker 。ActionInvoker經過CreateActionInvoker()方法來建立ControllerActionInvoker對象。
public IActionInvoker ActionInvoker { get { if (_actionInvoker == null) { _actionInvoker = CreateActionInvoker(); } return _actionInvoker; } set { _actionInvoker = value; } } protected virtual IActionInvoker CreateActionInvoker() { return new ControllerActionInvoker(); }
咱們看到CreateActionInvoker()是一個Virtual方法,咱們能夠實現本身的ActionInvoker.
ActionInvoker類須要匹配Controller中詳細的Action來執行,而這些詳細的信息是由ControllerDescriptor 提供的。ControllerDescriptor 和ActionDescriptor在ActionInvoker中扮演重要的角色。這兩個分別是對Controler和Action的詳細描述。ControllerDescriptor 描述了Controller的相關信息好比name,action,type等。
ActionDescriptor 描述了Action相關的詳情,好比name,controller,parameters,attributes和fiflters等。
ActionDescriptor 中一箇中要的方法就是FindAction(),這個方法返回一個ActionDescriptor 對象,因此ActionInvoker知道該調用哪一個Action。
到目前爲止,咱們看到了Action方法被ActionInvoker調用。全部的Action方法有一個特性,就是返回一個ActionResult類型的數據。
public abstract class ActionResult { public abstract void ExecuteResult(ControllerContext context); }
ExecuteResult()是一個抽象方法,因此不一樣的子類能夠提供不一樣的ExecuteResult()實現。
ActionResult執行後響應輸出到客戶端。
ViewResult幾乎是大部分應用程序的返回類型,主要經過ViewEngine引擎來展現view的。ViewEngine可能主要就是生成Html元素的引擎。Framwork提供了2種引擎,Razor View Engine 和Web Form View Engine.若是你想自定義引擎,你能夠建立一個引擎只要實現IViewEngine接口便可。
IViewEngine 有下面幾個方法:
一、FindPartialView :當controller須要返回一個PartialView的時候,FindPartialView方法 就會被調用。
二、FindView
三、ReleaseView :主要用來有ViewEngine釋放資源
ViewResultBase 和ViewResult是比較重要的兩個類。ViewResultBase 包含下面的實現代碼:
if (View == null) { result = FindView(context); //calls the ViewResult's FindView() method View = result.View; } ViewContext viewContext = new ViewContext(context, View, ViewData, TempData); View.Render(viewContext, context.HttpContext.Response.Output); protected abstract ViewEngineResult FindView(ControllerContext context); //this is implemented by //the ViewResult
protected override ViewEngineResult FindView(ControllerContext context) { ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName); if (result.View != null) { return result; } //rest of the code omitted }
當ViewResult的方法ExecuteResult被調用後,ViewResultBase 的ExecuteResult 方法被調用,而後ViewResultBase 調用ViewResult的FindView 。緊接着ViewResult 返回ViewEngineResult,以後ViewEngineResult調用Render()方法來繪製html輸出響應。
總結:若是咱們理解了整個過程當中發生了什麼,哪些類和哪些方法被調用,咱們就能夠在須要擴展的地方輕鬆的進行擴展。