MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)(转)
閱讀目錄
- 一、MVC原理解析
- 1、MVC原理
- 二、HttpHandler
- 1、HttpHandler、IHttpHandler、MvcHandler的說(shuō)明
- 2、IHttpHandler解析
- 3、MvcHandler解析
- 三、HttpModule
- 1、HttpModule能干什么
- 2、HttpModule的使用
- 3、HttpModule和HttpHandler如何區(qū)分
- 4、UrlRoutingModule解析
- ?四、總結(jié)
?
正文
前言:最近一段時(shí)間在學(xué)習(xí)MVC源碼,說(shuō)實(shí)話,研讀源碼真是一個(gè)痛苦的過(guò)程,好多晦澀的語(yǔ)法搞得人暈暈乎乎。這兩天算是理解了一小部分,這里先記錄下來(lái),也給需要的園友一個(gè)參考,奈何博主技術(shù)有限,如有理解不妥之處,還希望大家斧正,博主感激不盡!
本文原創(chuàng)地址:http://www.cnblogs.com/landeanfen/p/5989092.html
MVC源碼學(xué)習(xí)系列文章目錄:
- MVC系列——MVC源碼學(xué)習(xí):打造自己的MVC框架(一)
- MVC系列——MVC源碼學(xué)習(xí):打造自己的MVC框架(二:附源碼)
- MVC系列——MVC源碼學(xué)習(xí):打造自己的MVC框架(三:自定義路由規(guī)則)
- MVC系列——MVC源碼學(xué)習(xí):打造自己的MVC框架(四:自定義視圖)
一、MVC原理解析
?最近園子里Asp.Net Core火了一陣,不管微軟的開源動(dòng)作有多么遲緩,還是希望微軟能夠給力一次。作為Core的主要Web框架——MVC,雖然已經(jīng)開源,但是讀起來(lái)著實(shí)費(fèi)勁,并且感覺(jué)很多核心部件都找不到。于是只能通過(guò)Reflector去反編譯MVC5的組件以及參考博客園Fish Li等大神的文章去學(xué)習(xí)下MVC5的原理。
10月26日更新:感謝園友Adming在評(píng)論中提醒,原來(lái)Asp.net Core Mvc和Asp.net Mvc 5的原理已經(jīng)完全不同,難怪在Core Mvc的源碼里面已經(jīng)找不到MvcHandler、UrlRoutingModule等核心部件了呢,此系列文章就先學(xué)習(xí)下MVC5的原理,等以后有空了再來(lái)研究Core MVC吧。
Asp.Net Core MVC的開源地址:https://github.com/aspnet/Mvc
Asp.net MVC的開源地址:http://aspnetwebstack.codeplex.com/SourceControl/latest
回到頂部1、MVC原理
之前的文章有介紹MVC的路由機(jī)制,其實(shí)路由機(jī)制算是MVC的原理的核心之一。在此我們還是要不厭其煩再來(lái)談?wù)務(wù)麄€(gè)過(guò)程,因?yàn)檫@是理解MVC原理不可逾越的鴻溝。當(dāng)我們收到一個(gè)URL的請(qǐng)求時(shí),服務(wù)端收到請(qǐng)求,主要經(jīng)歷以下幾個(gè)步驟:
附上一張大致的流程圖:
縱觀整個(gè)過(guò)程,看上去很復(fù)雜,各種對(duì)象纏繞,看得人暈暈的。其實(shí)如果你靜下心來(lái)仔細(xì)研讀MVC的源碼你會(huì)發(fā)現(xiàn)其實(shí)并沒(méi)有想像中的那般復(fù)雜,請(qǐng)有點(diǎn)耐心聽(tīng)博主慢慢道來(lái)。
1、整個(gè)過(guò)程有兩個(gè)核心的組件,文中博主用紅色標(biāo)記了出來(lái):UrlRoutingModule和MvcHandler,上文提到的各個(gè)過(guò)程都和兩個(gè)組件有緊密的聯(lián)系。而這兩個(gè)組件分別繼承至IHttpModule和IHttpHandler接口,熟悉Asp.net管線事件的朋友應(yīng)該會(huì)記得這兩個(gè)接口,在管道事件里面這兩個(gè)接口扮演著重要角色。要理解MVC的上述原理,必須要先理解這兩類接口的原理以及使用。
2、UrlRoutingModule的作用可以理解為通過(guò)一系列的與路由相關(guān)的組件去解析當(dāng)前請(qǐng)求的Controller與Action名稱,其實(shí)簡(jiǎn)單點(diǎn)理解,比如我們請(qǐng)求http://localhost:8080/Home/Index這個(gè)url的時(shí)候,UrlRoutingModule攔截到這個(gè)請(qǐng)求,然后通過(guò)一系列的方式得到這里的“Home”和“Index”,這樣理解有沒(méi)有簡(jiǎn)單一點(diǎn)呢。
3、MvcHandler的作用就更加直接,上述通過(guò)攔截組件得到了請(qǐng)求的Controller和Action的名稱,MvcHandler組件將當(dāng)前請(qǐng)求的Controller名稱反射得到對(duì)應(yīng)的控制器對(duì)象,然后執(zhí)行對(duì)應(yīng)的Action方法。比如還是上述http://localhost:8080/Home/Index這個(gè)請(qǐng)求,通過(guò)字符串“Home”反射成為Home這個(gè)類型的控制器對(duì)象,然后調(diào)用這個(gè)對(duì)象的Index()方法。
4、綜上,聯(lián)合這兩個(gè)組件來(lái)理解,UrlRoutingMudule組件的主要作用是解析當(dāng)前的Controller與Action名稱,MvcHandler的作用是將得到的Controller名稱激活,得到具體的Controller對(duì)象,然后執(zhí)行對(duì)應(yīng)的Action方法。
所以,要理解MVC的原理,必須要了解這兩個(gè)組件的基本原理以及作用。下面就根據(jù)這兩個(gè)組件分別展開說(shuō)明,相信理解了下面的內(nèi)容,你對(duì)mvc的原理會(huì)有一個(gè)新的認(rèn)識(shí)。
回到頂部二、HttpHandler
上文說(shuō)過(guò)MvcHandler是繼承至IHttpHandler接口的!為什么這里大標(biāo)題會(huì)用HttpHandler而不是MvcHandler呢?因?yàn)椴┲饔X(jué)得,HttpHandler實(shí)在是太重要了,首先得理解了HttpHandler這么一個(gè)大的東西,然后再來(lái)看具體的MvcHandler才有意義。
回到頂部1、HttpHandler、IHttpHandler、MvcHandler的說(shuō)明
- HttpHandler指所有實(shí)現(xiàn)IHttpHandler接口一類類型的統(tǒng)稱,它是一個(gè)大的稱謂。這些類型有一個(gè)共同的功能,那就是可以用來(lái)處理Http請(qǐng)求。
- IHttpHandler是微軟定義的一類接口,用來(lái)約束所有能夠處理Http請(qǐng)求的類型的接口規(guī)則。
- MvcHandler是Mvc里面實(shí)現(xiàn)IHttpHandler接口的類型,也就是說(shuō),MvcHandler是Mvc里面處理Http請(qǐng)求的類型。
總而言之,HttpHandler只是一個(gè)邏輯稱謂,它并不具體存在。而IHttpHandler和MvcHandler是.net framework里面具體存在的接口和實(shí)現(xiàn)類,是前者的表現(xiàn)形式。
回到頂部2、IHttpHandler解析
?2.1、Asp.net管線事件簡(jiǎn)易說(shuō)明
做過(guò)Webform開發(fā)的園友應(yīng)該記得,在asp.net的頁(yè)面生命周期里面,一共有24個(gè)管線事件,完整的管線事件可參考MSDN文檔:
在處理該請(qǐng)求時(shí)將由 HttpApplication 類執(zhí)行以下事件。 希望擴(kuò)展 HttpApplication 類的開發(fā)人員尤其需要注意這些事件。 1. 對(duì)請(qǐng)求進(jìn)行驗(yàn)證,將檢查瀏覽器發(fā)送的信息,并確定其是否包含潛在惡意標(biāo)記。 有關(guān)更多信息,請(qǐng)參見(jiàn) ValidateRequest 和腳本侵入概述。 2. 如果已在 Web.config 文件的 UrlMappingsSection 節(jié)中配置了任何 URL,則執(zhí)行 URL 映射。 3. 引發(fā) BeginRequest 事件。 4. 引發(fā) AuthenticateRequest 事件。 5. 引發(fā) PostAuthenticateRequest 事件。 6. 引發(fā) AuthorizeRequest 事件。 7. 引發(fā) PostAuthorizeRequest 事件。 8. 引發(fā) ResolveRequestCache 事件。 9. 引發(fā) PostResolveRequestCache 事件。 10. 根據(jù)所請(qǐng)求資源的文件擴(kuò)展名(在應(yīng)用程序的配置文件中映射),選擇實(shí)現(xiàn) IHttpHandler 的類,對(duì)請(qǐng)求進(jìn)行處理。 如果該請(qǐng)求針對(duì)從 Page 類派生的對(duì)象(頁(yè)),并且需要對(duì)該頁(yè)進(jìn)行編譯,則 ASP.NET 會(huì)在創(chuàng)建該頁(yè)的實(shí)例之前對(duì)其進(jìn)行編譯。 11. 引發(fā) PostMapRequestHandler 事件。 12. 引發(fā) AcquireRequestState 事件。 13. 引發(fā) PostAcquireRequestState 事件。 14. 引發(fā) PreRequestHandlerExecute 事件。 15. 為該請(qǐng)求調(diào)用合適的 IHttpHandler 類的 ProcessRequest 方法(或異步版 IHttpAsyncHandler.BeginProcessRequest)。 例如,如果該請(qǐng)求針對(duì)某頁(yè),則當(dāng)前的頁(yè)實(shí)例將處理該請(qǐng)求。 16. 引發(fā) PostRequestHandlerExecute 事件。 17. 引發(fā) ReleaseRequestState 事件。 18. 引發(fā) PostReleaseRequestState 事件。 19. 如果定義了 Filter 屬性,則執(zhí)行響應(yīng)篩選。 20. 引發(fā) UpdateRequestCache 事件。 21. 引發(fā) PostUpdateRequestCache 事件。 22. 引發(fā) EndRequest 事件。 23. 引發(fā) PreSendRequestHeaders 事件。 24. 引發(fā) PreSendRequestContent 事件。這里不可能把每個(gè)管線事件將清楚,但是在整個(gè)管線事件中,有兩個(gè)重要的角色就是HttpHandler和HttpModule。在這些事件中,第10個(gè)事件【根據(jù)所請(qǐng)求資源的文件擴(kuò)展名(在應(yīng)用程序的配置文件中映射),選擇實(shí)現(xiàn) IHttpHandler 的類,對(duì)請(qǐng)求進(jìn)行處理】 是HttpHandler創(chuàng)建的地方。關(guān)于WebForm里面HttpHandler創(chuàng)建的詳細(xì)過(guò)程,這里就不展開說(shuō)了,如果有興趣可以參考http://www.cnblogs.com/fish-li/archive/2012/01/29/2331477.html。
2.2、Asp.net中常見(jiàn)的HttpHandler類型
首先還是來(lái)看看IHttpHandler的定義
public interface IHttpHandler {// 定義一個(gè)處理當(dāng)前http請(qǐng)求的方法void ProcessRequest(HttpContext context); // 指示當(dāng)前實(shí)例是否可以再次使用 bool IsReusable { get; } }接口的定義很簡(jiǎn)單,ProcessRequest()方法里面?zhèn)饕粋€(gè)當(dāng)前請(qǐng)求的上下文對(duì)象去處理當(dāng)前的http請(qǐng)求。
為了處理異步請(qǐng)求,Framework里面還定義了一個(gè)異步的IHttpHandler接口:
public interface IHttpAsyncHandler : IHttpHandler {// MethodsIAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData); void EndProcessRequest(IAsyncResult result); }接口的兩個(gè)方法應(yīng)該也不難理解。
我們已經(jīng)說(shuō)了,HttpHandler的主要作用是處理http請(qǐng)求,原來(lái)在做webform的時(shí)候應(yīng)該都寫過(guò)后綴ashx的一般處理程序吧,這個(gè)一般處理程序就是通過(guò)實(shí)現(xiàn)IHttpHandler接口去實(shí)現(xiàn)的。我們是否曾經(jīng)也寫過(guò)類似這樣的代碼,新建一個(gè)TestHttpHandler.ashx文件,代碼如下:
public class TestHttpHandler : IHttpHandler{public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; var username = context.Request.QueryString["username"]; var password = context.Request.QueryString["password"]; if (username == "admin" && password == "admin") { context.Response.Write("用戶admin登錄成功"); } else { context.Response.Write("用戶名或者密碼錯(cuò)誤"); } } public bool IsReusable { get { return false; } } }然后運(yùn)行,通過(guò)http://localhost:16792/TestHttpHandler.ashx?username=admin&password=admin去訪問(wèn)一般處理程序,即可得到正確的結(jié)果。
當(dāng)然,除了這個(gè),還有我們最常見(jiàn)的aspx頁(yè)面。
public partial class TestPage : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e) { } }將Page類轉(zhuǎn)到定義:
發(fā)現(xiàn)原來(lái)Page類也是繼承至IHttpHandler,這就是為什么我們可以通過(guò)地址http://localhost:16792/TestPage.aspx來(lái)訪問(wèn)這個(gè)頁(yè)面的原因。當(dāng)然,子類中的ProcessRequest()方法并沒(méi)有顯示的聲明出來(lái),因?yàn)樵赑age類里面已經(jīng)有一個(gè)virtue的虛方法,如果需要,你也可以在TestPage這個(gè)類里面顯示聲明:
public partial class TestPage : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e) { } public void ProcessRequest(HttpContext context) { context.Response.Write("你好"); } }然后你會(huì)發(fā)現(xiàn)這個(gè)時(shí)候請(qǐng)求會(huì)進(jìn)到ProcessRequest()方法,而不會(huì)進(jìn)到Page_Load()里面了,至于原因,這和Page類里面的封裝有關(guān)系。當(dāng)然這不是本文的重點(diǎn),本文要說(shuō)明的是所有實(shí)現(xiàn)了IHttpHandler接口的類型都可以在ProcessRequest()方法里面處理當(dāng)前http請(qǐng)求。
當(dāng)然,除了ashx和aspx以外,還有一類http的服務(wù)接口處理文件asmx也和IHttpHandler有著不可分割的聯(lián)系,可以說(shuō),在asp.net里面,只要是處理Http請(qǐng)求的地方,IHttpHandler幾乎“無(wú)處不在”。
2.3、自定義HttpHandler。
當(dāng)然,除了上述asp.net自帶的HttpHandler之外,我們也可以自定義HttpHandler處理特定的請(qǐng)求。比如我們新建一個(gè)TestMyHandler.cs頁(yè)面:
public class TestMyHandler:IHttpHandler{public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { context.Response.Write("從asex頁(yè)面進(jìn)來(lái)"); //throw new NotImplementedException(); } }當(dāng)然,要使用這個(gè)自定義的Handler需要在web.config里面加上配置。(PS:這部分是博主后來(lái)加上的,所以直接用正確的配置)
<system.webServer><handlers> <add name="asex" verb="*" path="*.asex" type="MyTestMVC.TestMyHandler, MyTestMVC" preCondition="integratedMode" /> </handlers> </system.webServer>這個(gè)配置的意思是所有的url以asex結(jié)尾的請(qǐng)求都交給TestMyHandler這個(gè)類去處理。得到效果:
回到頂部3、MvcHandler解析
上文介紹了那么多IHttpHandler的用法,都是在WebForm里面的一些實(shí)現(xiàn),我們知道了所有實(shí)現(xiàn)了IHttpHandler的類都可以處理Http請(qǐng)求。同樣在MVC里面,也定義了一個(gè)實(shí)現(xiàn)IHttpHandler接口的類型——MvcHandler,用于處理當(dāng)前的http請(qǐng)求。通過(guò)反編譯工具可以看到:
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState {// 省略若干字段// 所有方法 static MvcHandler(); public MvcHandler(RequestContext requestContext); protected internal virtual void AddVersionHeader(HttpContextBase httpContext); protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state); protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state); protected internal virtual void EndProcessRequest(IAsyncResult asyncResult); private static string GetMvcVersionString(); protected virtual void ProcessRequest(HttpContext httpContext); protected internal virtual void ProcessRequest(HttpContextBase httpContext); private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory); private void RemoveOptionalRoutingParameters(); IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData); void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result); void IHttpHandler.ProcessRequest(HttpContext httpContext); // 省略若干屬性 }MvcHandler實(shí)現(xiàn)了IHttpHandler、 IHttpAsyncHandler兩個(gè)接口,異步請(qǐng)求這里先不做介紹。重點(diǎn)還是來(lái)看看ProcessRequest()方法
將HttpContext轉(zhuǎn)換為HttpContextBase對(duì)象,繼續(xù)轉(zhuǎn)到定義。
這里聲明了一個(gè)IController和IControllerFactory對(duì)象,通過(guò)this.ProcessRequestInit()方法創(chuàng)建具體的Controller實(shí)例。我們將ProcessRequestInit()方法轉(zhuǎn)到定義
我們將代碼復(fù)制出來(lái),寫入相應(yīng)的注釋:
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory){//1.得到當(dāng)前的上下文 HttpContext current = HttpContext.Current; if (current != null && ValidationUtility.IsValidationEnabled(current) == true) ValidationUtility.EnableDynamicValidation(current); this.AddVersionHeader(httpContext); this.RemoveOptionalRoutingParameters(); //2.從路由對(duì)象RouteData中獲取當(dāng)前請(qǐng)求的Controller名稱 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); //3.得到Controller工廠對(duì)象 factory = this.ControllerBuilder.GetControllerFactory(); //4.根據(jù)當(dāng)前RequestContext對(duì)象,從Controller工廠創(chuàng)建具體的Controller對(duì)象 controller = factory.CreateController(this.RequestContext, requiredString); if (controller == null) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString })); }通過(guò)上文的注釋很好理解整個(gè)控制器的實(shí)例化過(guò)程。本打算看下Controller工廠如何創(chuàng)建以及控制器如何實(shí)例化的,奈何這部分反編譯不了。我們暫且理解為反射吧,這些實(shí)現(xiàn)細(xì)節(jié)并不影響我們理解整個(gè)過(guò)程。
創(chuàng)建控制器成功之后,就是執(zhí)行Action方法了,這個(gè)過(guò)程在上面反編譯的第二張圖片的?controller.Execute(this.RequestContext);?方法得到體現(xiàn)。所以,除去細(xì)節(jié),理解MvcHandler的ProcessRequest()方法并不是太難。
回到頂部三、HttpModule
除了HttpHandler之外,Asp.net里面還有另外一個(gè)重要的角色——HttpModule。和HttpHandler類似,HttpModule指所有實(shí)現(xiàn)了IHttpModule接口的一類類型的統(tǒng)稱。至于HttpModule、IHttpModule、UrlRoutingModule各個(gè)名稱的含義和上述HttpHandler相同,在此不做重復(fù)說(shuō)明。
回到頂部1、HttpModule能干什么
通過(guò)上文,我們知道HttpHandler的作用非常明確:處理Http請(qǐng)求,生成相應(yīng)結(jié)果。那么,HttpModule又是干什么的呢?
HttpHandler的作用是處理某一類別的請(qǐng)求,比如ashx、aspx、asmx等,在某些情況下,各類請(qǐng)求可能都需要進(jìn)行某些相同的處理(比如請(qǐng)求攔截、身份認(rèn)證、檢查功能等),不可能在每個(gè)類別的HttpHandler里面都去實(shí)現(xiàn)這些相同的代碼,這個(gè)時(shí)候怎么辦呢?處理某一類通用請(qǐng)求,提高代碼的復(fù)用率。是不是想到了我們的面向切面編程(AOP),沒(méi)錯(cuò),HttpModule就是負(fù)責(zé)做這個(gè)事,HttpModule通過(guò)事件訂閱的方式,將某類HttpHandler都需要的功能抽取出來(lái),這些功能可以編譯成類庫(kù)供各個(gè)模塊調(diào)用。這種采用事件(觀察者)的設(shè)計(jì)模式使得系統(tǒng)設(shè)計(jì)上更加靈活。
回到頂部2、HttpModule的使用
先來(lái)看看IHttpModule的定義
public interface IHttpModule {//初始化void Init(HttpApplication context); //釋放 void Dispose(); }接口定義很簡(jiǎn)單,一個(gè)初始化組件的方法,一個(gè)釋放對(duì)象的方法。
我們來(lái)寫一個(gè)測(cè)試的例子具體看看HttpModule如何注冊(cè)事件,我們新建一個(gè)IHttpModule的實(shí)現(xiàn)類:
namespace MyTestMVC {public class TestMyModule:IHttpModule{public void Dispose() { //throw new NotImplementedException(); } public void Init(HttpApplication app) { //事件注冊(cè) app.BeginRequest += app_BeginRequest; app.EndRequest += app_EndRequest; } void app_EndRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; app.Context.Response.Write("請(qǐng)求結(jié)束"); } void app_BeginRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; app.Context.Response.Write("請(qǐng)求開始"); } } }在Init方法里面,通過(guò)HttpApplication對(duì)象來(lái)注冊(cè)請(qǐng)求的事件。這樣,每次發(fā)起一次http請(qǐng)求的時(shí)候都進(jìn)到這兩個(gè)方法。
當(dāng)然,這些注冊(cè)就能執(zhí)行了嗎?想得美,系統(tǒng)哪里知道你這個(gè)自定義HttpModule的存在,所以必須要在Web.config里面聲明一下。
<system.web><httpModules> <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" /> </httpModules> </system.web>出現(xiàn)結(jié)果:
查閱資料后發(fā)現(xiàn),原來(lái)IIS經(jīng)典模式下必須要這樣配置:
<system.webServer><modules> <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" preCondition="integratedMode" /> </modules> </system.webServer>沒(méi)辦法,用微軟的東西就要遵守別人的游戲規(guī)則。改成這樣之后得到結(jié)果:
文中的“你好”來(lái)自這里:
?既然HttpModule是事件注冊(cè)機(jī)制的,那么如果需要在同一個(gè)事件里面去實(shí)現(xiàn)不同的功能,也就是說(shuō)同一個(gè)事件注冊(cè)多次是否可行呢?我們來(lái)試一把:
假如TestMyModule.cs這個(gè)自定義Module的作用是功能檢查:
public class TestMyModule:IHttpModule{public void Dispose() { //throw new NotImplementedException(); } public void Init(HttpApplication app) { //事件注冊(cè) app.BeginRequest += app_BeginRequest; app.EndRequest += app_EndRequest; } void app_EndRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; app.Context.Response.Write("功能檢查結(jié)束"); } void app_BeginRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; app.Context.Response.Write("功能檢查開始"); //功能檢查的處理邏輯... } }然后新建一個(gè)TestMyModule2.cs這個(gè)自定義Module,去實(shí)現(xiàn)請(qǐng)求攔截的功能:
public class TestMyModule2:IHttpModule{public void Dispose() { //throw new NotImplementedException(); } public void Init(HttpApplication app) { //事件注冊(cè) app.BeginRequest += app_BeginRequest; app.EndRequest += app_EndRequest; } void app_EndRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; app.Context.Response.Write("請(qǐng)求攔截結(jié)束"); } void app_BeginRequest(object sender, EventArgs e) { var app = (HttpApplication)sender; app.Context.Response.Write("請(qǐng)求攔截開始"); //請(qǐng)求攔截的處理邏輯.... } }最后在Web.config里面配置兩個(gè)Module:
<system.webServer><modules> <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" preCondition="integratedMode" /> <add name="TestMyModule2" type="MyTestMVC.TestMyModule2, MyTestMVC" preCondition="integratedMode" /> </modules> </system.webServer>得到結(jié)果:
?
這說(shuō)明同一個(gè)事件可以注冊(cè)多次,即可以在同一個(gè)事件里面做不同的事。
回到頂部3、HttpModule和HttpHandler如何區(qū)分
通過(guò)上文的HttpModule的應(yīng)用,我們看到在Init方法里面可以拿到當(dāng)前應(yīng)用的HttpApplication對(duì)象,拿到這個(gè)貌似就可以拿到當(dāng)前請(qǐng)求上下文里面的Request、Response了,是不是就可以處理當(dāng)前的http請(qǐng)求了,從這點(diǎn)上來(lái)說(shuō),HttpModule也能處理http請(qǐng)求,或者說(shuō)具有處理http請(qǐng)求的能力。既然HttpHandler和HttpModule都可以處理http請(qǐng)求,那在使用的時(shí)候如何區(qū)分呢?上文說(shuō)過(guò),HttpModule的作用類似AOP,是針對(duì)某些通用功能(請(qǐng)求攔截、身份認(rèn)證、檢查功能)的,而HttpHandler常用來(lái)處理某一類(ashx、aspx、asmx)http請(qǐng)求,兩者的側(cè)重點(diǎn)不同,至于具體在實(shí)際中如何使用,你可以自行考量。
回到頂部4、UrlRoutingModule解析
好了,上面介紹那么多HttpModule的使用,都是在為了解Mvc里面的UrlRoutingModule做鋪墊。上文說(shuō)過(guò)UrlRoutingModule的作用是攔截請(qǐng)求,那么它是如何做的呢,還是來(lái)反編譯看看吧。
public class UrlRoutingModule : IHttpModule {// Fieldsprivate static readonly object _contextKey; private static readonly object _requestDataKey; private RouteCollection _routeCollection; // Methods static UrlRoutingModule(); public UrlRoutingModule(); protected virtual void Dispose(); protected virtual void Init(HttpApplication application); private void OnApplicationPostResolveRequestCache(object sender, EventArgs e); [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); void IHttpModule.Dispose(); void IHttpModule.Init(HttpApplication application); // Properties public RouteCollection RouteCollection { get; set; } }重點(diǎn)肯定在Init()方法。
圖一:
注冊(cè)HttpApplication對(duì)象的PostResolveRequestCache事件。
圖二:
封裝HttpContext,成為HttpContextWrapper對(duì)象
圖三:
這部分代碼是我們上述路由理論的代碼實(shí)踐,所以這段代碼很重要,我們將代碼拷貝出來(lái):
public virtual void PostResolveRequestCache(HttpContextBase context){//1.傳入當(dāng)前上下文對(duì)象,得到與當(dāng)前請(qǐng)求匹配的RouteData對(duì)象 RouteData routeData = this.RouteCollection.GetRouteData(context); if (routeData != null) { //2.從RouteData對(duì)象里面得到當(dāng)前的RouteHandler對(duì)象。其實(shí)這里的RouteHandler屬性對(duì)應(yīng)就是一個(gè)MvcRouteHandler的對(duì)象。 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)) { //3.根據(jù)HttpContext和RouteData得到RequestContext對(duì)象 RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; //4.根據(jù)RequestContext對(duì)象得到處理當(dāng)前請(qǐng)求的HttpHandler(MvcHandler)。 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (httpHandler == null) { object[] args = new object[] { routeHandler.GetType() }; throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), args)); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3")); UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); } else //5.請(qǐng)求轉(zhuǎn)到HttpHandler進(jìn)行處理(進(jìn)入到ProcessRequest方法)。這一步很重要,由這一步開始,請(qǐng)求才由UrlRoutingModule轉(zhuǎn)到了MvcHandler里面 context.RemapHandler(httpHandler); } } }博主在主要的地方加上了注釋。
代碼釋疑:這里有幾點(diǎn)需要說(shuō)明的。
1、HttpApplication對(duì)象的PostResolveRequestCache事件在MSDN上的解釋是:在 ASP.NET 跳過(guò)當(dāng)前事件處理程序的執(zhí)行并允許緩存模塊滿足來(lái)自緩存的請(qǐng)求時(shí)發(fā)生。查閱相關(guān)資料發(fā)現(xiàn),之所以在PostResolveRequestCache事件注冊(cè)路由、匹配HttpHandler,是為了滿足IIS6。可以參考Tom大叔的文章:http://www.cnblogs.com/TomXu/p/3756858.html。
2、?IRouteHandler routeHandler = routeData.RouteHandler;?這里的routeHandler實(shí)際上是一個(gè)MvcRouteHandler類型的對(duì)象,為什么這么說(shuō),我們來(lái)反編譯下這個(gè)就會(huì)一目了然:
圖一:
?
MvcRouteHandler實(shí)現(xiàn)了IRouteHandler接口。然后我們重點(diǎn)來(lái)看GetHttpHandler()方法得到的是哪個(gè)HttpHandler。
圖二:
?
看到最后一句是不是立馬就明白了。也就是說(shuō)GetHttpHandler()這個(gè)方法決定了采用MvcHandler去處理當(dāng)前的http請(qǐng)求。所以在上述?IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);?這一句得到的就是一個(gè)MvcHandler的實(shí)例。
3、?context.RemapHandler(httpHandler);?這一句可以理解為將當(dāng)前的請(qǐng)求上下文交給httpHandler這個(gè)對(duì)象去處理。
4、到這里,我們?cè)俜催^(guò)來(lái)看前面的MVC的原理就完全明朗了。
?四、總結(jié)
寫到這里,總算把整個(gè)過(guò)程梳理了一遍,很多細(xì)節(jié)都未涉及,但是大的過(guò)程應(yīng)該還是明朗的。通篇比較偏理論,所以整體上比較枯燥,但是還是希望園友們能夠靜下心來(lái)慢慢看,因?yàn)椴┲饔X(jué)得這些對(duì)于理解MVC原理太重要!!!想想看,如果你也完全理解了這個(gè)過(guò)程,是不是都可以自己通過(guò)實(shí)現(xiàn)IHttphandler和IHttpModule去搭建一個(gè)簡(jiǎn)單的MVC框架了,不錯(cuò),博主確實(shí)是這樣打算的,這篇把理論搞清楚,下篇就是實(shí)現(xiàn)的細(xì)節(jié)了。其實(shí)寫自己的MVC框架更多的在于學(xué)習(xí)MVC原理,希望自己能夠堅(jiān)持下去。如果你覺(jué)得本文能夠幫助你,可以右邊隨意?打賞?博主,也可以?推薦?進(jìn)行精神鼓勵(lì)。你的支持是博主繼續(xù)堅(jiān)持的不懈動(dòng)力。
本文原創(chuàng)出處:http://www.cnblogs.com/landeanfen/
歡迎各位轉(zhuǎn)載,但是未經(jīng)作者本人同意,轉(zhuǎn)載文章之后必須在文章頁(yè)面明顯位置給出作者和原文連接,否則保留追究法律責(zé)任的權(quán)利
源碼下載 密碼juw2
轉(zhuǎn)載于:https://www.cnblogs.com/yxhblog/p/7498709.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: web移动端开发经验总结
- 下一篇: [转载]sql server 分布式查询