MVC系列——MVC源碼學習:打造自己的MVC框架(一:核心原理)


前言:最近一段時間在學習MVC源碼,說實話,研讀源碼真是一個痛苦的過程,好多晦澀的語法搞得人暈暈乎乎。這兩天算是理解了一小部分,這里先記錄下來,也給需要的園友一個參考,奈何博主技術有限,如有理解不妥之處,還希望大家斧正,博主感激不盡!

本文原創地址:http://www.cnblogs.com/landeanfen/p/5989092.html

MVC源碼學習系列文章目錄:

一、MVC原理解析

 最近園子里Asp.Net Core火了一陣,不管微軟的開源動作有多么遲緩,還是希望微軟能夠給力一次。作為Core的主要Web框架——MVC,雖然已經開源,但是讀起來着實費勁,並且感覺很多核心部件都找不到。於是只能通過Reflector去反編譯MVC5的組件以及參考博客園Fish Li等大神的文章去學習下MVC5的原理。

10月26日更新:感謝園友Adming在評論中提醒,原來Asp.net Core Mvc和Asp.net Mvc 5的原理已經完全不同,難怪在Core Mvc的源碼里面已經找不到MvcHandler、UrlRoutingModule等核心部件了呢,此系列文章就先學習下MVC5的原理,等以后有空了再來研究Core MVC吧。

Asp.Net Core MVC的開源地址:https://github.com/aspnet/Mvc

Asp.net MVC的開源地址:http://aspnetwebstack.codeplex.com/SourceControl/latest

1、MVC原理

之前的文章有介紹MVC的路由機制,其實路由機制算是MVC的原理的核心之一。在此我們還是要不厭其煩再來談談整個過程,因為這是理解MVC原理不可逾越的鴻溝。當我們收到一個URL的請求時,服務端收到請求,主要經歷以下幾個步驟:

  1. 請求被UrlRoutingModule部件攔截
  2. 封裝請求上下文HttpContext,成為HttpContextWrapper對象。
  3. 根據當前的HttpContext,從Routes集合中得到與當前請求URL相符合的RouteData對象。
  4. RouteDataHttpContext請求封裝成一個RequestContext對象。
  5. 根據RequestContext對象,從RouteData的RouteHandler中獲取IHttpHandler(MVC里面會有一個IHttpHandler的實現類MvcHandler)。
  6. 執行IHttpHandler(MvcHandler),然后就是通過反射激活具體的controller,執行具體的action。

附上一張大致的流程圖:

縱觀整個過程,看上去很復雜,各種對象纏繞,看得人暈暈的。其實如果你靜下心來仔細研讀MVC的源碼你會發現其實並沒有想像中的那般復雜,請有點耐心聽博主慢慢道來。

1、整個過程有兩個核心的組件,文中博主用紅色標記了出來:UrlRoutingModuleMvcHandler,上文提到的各個過程都和兩個組件有緊密的聯系。而這兩個組件分別繼承至IHttpModule和IHttpHandler接口,熟悉Asp.net管線事件的朋友應該會記得這兩個接口,在管道事件里面這兩個接口扮演着重要角色。要理解MVC的上述原理,必須要先理解這兩類接口的原理以及使用。

2、UrlRoutingModule的作用可以理解為通過一系列的與路由相關的組件去解析當前請求的Controller與Action名稱,其實簡單點理解,比如我們請求http://localhost:8080/Home/Index這個url的時候,UrlRoutingModule攔截到這個請求,然后通過一系列的方式得到這里的“Home”和“Index”,這樣理解有沒有簡單一點呢。

3、MvcHandler的作用就更加直接,上述通過攔截組件得到了請求的Controller和Action的名稱,MvcHandler組件將當前請求的Controller名稱反射得到對應的控制器對象,然后執行對應的Action方法。比如還是上述http://localhost:8080/Home/Index這個請求,通過字符串“Home”反射成為Home這個類型的控制器對象,然后調用這個對象的Index()方法。

4、綜上,聯合這兩個組件來理解,UrlRoutingMudule組件的主要作用是解析當前的Controller與Action名稱,MvcHandler的作用是將得到的Controller名稱激活,得到具體的Controller對象,然后執行對應的Action方法。

所以,要理解MVC的原理,必須要了解這兩個組件的基本原理以及作用。下面就根據這兩個組件分別展開說明,相信理解了下面的內容,你對mvc的原理會有一個新的認識。

二、HttpHandler

上文說過MvcHandler是繼承至IHttpHandler接口的!為什么這里大標題會用HttpHandler而不是MvcHandler呢?因為博主覺得,HttpHandler實在是太重要了,首先得理解了HttpHandler這么一個大的東西,然后再來看具體的MvcHandler才有意義。

1、HttpHandler、IHttpHandler、MvcHandler的說明

  • HttpHandler指所有實現IHttpHandler接口一類類型的統稱,它是一個大的稱謂。這些類型有一個共同的功能,那就是可以用來處理Http請求。
  • IHttpHandler是微軟定義的一類接口,用來約束所有能夠處理Http請求的類型的接口規則。
  • MvcHandler是Mvc里面實現IHttpHandler接口的類型,也就是說,MvcHandler是Mvc里面處理Http請求的類型。

總而言之,HttpHandler只是一個邏輯稱謂,它並不具體存在。而IHttpHandler和MvcHandler是.net framework里面具體存在的接口和實現類,是前者的表現形式。

2、IHttpHandler解析

 2.1、Asp.net管線事件簡易說明

做過Webform開發的園友應該記得,在asp.net的頁面生命周期里面,一共有24個管線事件,完整的管線事件可參考MSDN文檔:

在處理該請求時將由 HttpApplication 類執行以下事件。 希望擴展 HttpApplication 類的開發人員尤其需要注意這些事件。
1. 對請求進行驗證,將檢查瀏覽器發送的信息,並確定其是否包含潛在惡意標記。 有關更多信息,請參見 ValidateRequest 和腳本侵入概述。
2. 如果已在 Web.config 文件的 UrlMappingsSection 節中配置了任何 URL,則執行 URL 映射。
3. 引發 BeginRequest 事件。
4. 引發 AuthenticateRequest 事件。
5. 引發 PostAuthenticateRequest 事件。
6. 引發 AuthorizeRequest 事件。
7. 引發 PostAuthorizeRequest 事件。
8. 引發 ResolveRequestCache 事件。
9. 引發 PostResolveRequestCache 事件。
10. 根據所請求資源的文件擴展名(在應用程序的配置文件中映射),選擇實現 IHttpHandler 的類,對請求進行處理。 如果該請求針對從 Page 類派生的對象(頁),並且需要對該頁進行編譯,則 ASP.NET 會在創建該頁的實例之前對其進行編譯。
11. 引發 PostMapRequestHandler 事件。
12. 引發 AcquireRequestState 事件。
13. 引發 PostAcquireRequestState 事件。
14. 引發 PreRequestHandlerExecute 事件。
15. 為該請求調用合適的 IHttpHandler 類的 ProcessRequest 方法(或異步版 IHttpAsyncHandler.BeginProcessRequest)。 例如,如果該請求針對某頁,則當前的頁實例將處理該請求。 
16. 引發 PostRequestHandlerExecute 事件。
17. 引發 ReleaseRequestState 事件。
18. 引發 PostReleaseRequestState 事件。
19. 如果定義了 Filter 屬性,則執行響應篩選。
20. 引發 UpdateRequestCache 事件。
21. 引發 PostUpdateRequestCache 事件。
22. 引發 EndRequest 事件。
23. 引發 PreSendRequestHeaders 事件。
24. 引發 PreSendRequestContent 事件。
Asp.net管線事件說明

這里不可能把每個管線事件將清楚,但是在整個管線事件中,有兩個重要的角色就是HttpHandlerHttpModule。在這些事件中,第10個事件【根據所請求資源的文件擴展名(在應用程序的配置文件中映射),選擇實現 IHttpHandler 的類,對請求進行處理】 是HttpHandler創建的地方。關於WebForm里面HttpHandler創建的詳細過程,這里就不展開說了,如果有興趣可以參考http://www.cnblogs.com/fish-li/archive/2012/01/29/2331477.html

2.2、Asp.net中常見的HttpHandler類型

首先還是來看看IHttpHandler的定義

public interface IHttpHandler
{
    // 定義一個處理當前http請求的方法
    void ProcessRequest(HttpContext context);

    // 指示當前實例是否可以再次使用
    bool IsReusable { get; }
}

 

接口的定義很簡單,ProcessRequest()方法里面傳一個當前請求的上下文對象去處理當前的http請求。

為了處理異步請求,Framework里面還定義了一個異步的IHttpHandler接口:

public interface IHttpAsyncHandler : IHttpHandler
{
    // Methods
    IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
    void EndProcessRequest(IAsyncResult result);
}

接口的兩個方法應該也不難理解。

我們已經說了,HttpHandler的主要作用是處理http請求,原來在做webform的時候應該都寫過后綴ashx的一般處理程序吧,這個一般處理程序就是通過實現IHttpHandler接口去實現的。我們是否曾經也寫過類似這樣的代碼,新建一個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("用戶名或者密碼錯誤");
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }

然后運行,通過http://localhost:16792/TestHttpHandler.ashx?username=admin&password=admin去訪問一般處理程序,即可得到正確的結果。

當然,除了這個,還有我們最常見的aspx頁面。

    public partial class TestPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
    }

將Page類轉到定義:

發現原來Page類也是繼承至IHttpHandler,這就是為什么我們可以通過地址http://localhost:16792/TestPage.aspx來訪問這個頁面的原因。當然,子類中的ProcessRequest()方法並沒有顯示的聲明出來,因為在Page類里面已經有一個virtue的虛方法,如果需要,你也可以在TestPage這個類里面顯示聲明:

    public partial class TestPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("你好");
        }
    }

然后你會發現這個時候請求會進到ProcessRequest()方法,而不會進到Page_Load()里面了,至於原因,這和Page類里面的封裝有關系。當然這不是本文的重點,本文要說明的是所有實現了IHttpHandler接口的類型都可以在ProcessRequest()方法里面處理當前http請求。

當然,除了ashx和aspx以外,還有一類http的服務接口處理文件asmx也和IHttpHandler有着不可分割的聯系,可以說,在asp.net里面,只要是處理Http請求的地方,IHttpHandler幾乎“無處不在”。

2.3、自定義HttpHandler。

當然,除了上述asp.net自帶的HttpHandler之外,我們也可以自定義HttpHandler處理特定的請求。比如我們新建一個TestMyHandler.cs頁面:

    public class TestMyHandler:IHttpHandler
    {
        public bool IsReusable
        {
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("從asex頁面進來");

            //throw new NotImplementedException();
        }
    }

當然,要使用這個自定義的Handler需要在web.config里面加上配置。(PS:這部分是博主后來加上的,所以直接用正確的配置)

<system.webServer>
   <handlers>
        <add name="asex" verb="*" path="*.asex" type="MyTestMVC.TestMyHandler, MyTestMVC" preCondition="integratedMode" />
    </handlers>
</system.webServer>

這個配置的意思是所有的url以asex結尾的請求都交給TestMyHandler這個類去處理。得到效果:

3、MvcHandler解析

上文介紹了那么多IHttpHandler的用法,都是在WebForm里面的一些實現,我們知道了所有實現了IHttpHandler的類都可以處理Http請求。同樣在MVC里面,也定義了一個實現IHttpHandler接口的類型——MvcHandler,用於處理當前的http請求。通過反編譯工具可以看到:

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實現了IHttpHandler、 IHttpAsyncHandler兩個接口,異步請求這里先不做介紹。重點還是來看看ProcessRequest()方法

將HttpContext轉換為HttpContextBase對象,繼續轉到定義。

這里聲明了一個IController和IControllerFactory對象,通過this.ProcessRequestInit()方法創建具體的Controller實例。我們將ProcessRequestInit()方法轉到定義

我們將代碼復制出來,寫入相應的注釋:

     private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
        {
            //1.得到當前的上下文
            HttpContext current = HttpContext.Current;
            if (current != null && ValidationUtility.IsValidationEnabled(current) == true) ValidationUtility.EnableDynamicValidation(current);
            this.AddVersionHeader(httpContext);
            this.RemoveOptionalRoutingParameters();

            //2.從路由對象RouteData中獲取當前請求的Controller名稱
            string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");

            //3.得到Controller工廠對象
            factory = this.ControllerBuilder.GetControllerFactory();

            //4.根據當前RequestContext對象,從Controller工廠創建具體的Controller對象
            controller = factory.CreateController(this.RequestContext, requiredString);
            if (controller == null) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString }));
        }

通過上文的注釋很好理解整個控制器的實例化過程。本打算看下Controller工廠如何創建以及控制器如何實例化的,奈何這部分反編譯不了。我們暫且理解為反射吧,這些實現細節並不影響我們理解整個過程。

創建控制器成功之后,就是執行Action方法了,這個過程在上面反編譯的第二張圖片的 controller.Execute(this.RequestContext); 方法得到體現。所以,除去細節,理解MvcHandler的ProcessRequest()方法並不是太難。

三、HttpModule

除了HttpHandler之外,Asp.net里面還有另外一個重要的角色——HttpModule。和HttpHandler類似,HttpModule指所有實現了IHttpModule接口的一類類型的統稱。至於HttpModule、IHttpModule、UrlRoutingModule各個名稱的含義和上述HttpHandler相同,在此不做重復說明。

1、HttpModule能干什么

通過上文,我們知道HttpHandler的作用非常明確:處理Http請求,生成相應結果。那么,HttpModule又是干什么的呢?

HttpHandler的作用是處理某一類別的請求,比如ashx、aspx、asmx等,在某些情況下,各類請求可能都需要進行某些相同的處理(比如請求攔截、身份認證、檢查功能等),不可能在每個類別的HttpHandler里面都去實現這些相同的代碼,這個時候怎么辦呢?處理某一類通用請求,提高代碼的復用率。是不是想到了我們的面向切面編程(AOP),沒錯,HttpModule就是負責做這個事,HttpModule通過事件訂閱的方式,將某類HttpHandler都需要的功能抽取出來,這些功能可以編譯成類庫供各個模塊調用。這種采用事件(觀察者)的設計模式使得系統設計上更加靈活。

2、HttpModule的使用

先來看看IHttpModule的定義

public interface IHttpModule
{
    //初始化
    void Init(HttpApplication context);
   
    //釋放
    void Dispose();
}

接口定義很簡單,一個初始化組件的方法,一個釋放對象的方法。

我們來寫一個測試的例子具體看看HttpModule如何注冊事件,我們新建一個IHttpModule的實現類:

namespace MyTestMVC
{
    public class TestMyModule:IHttpModule
    {
        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication app)
        {
            //事件注冊
            app.BeginRequest += app_BeginRequest;
            app.EndRequest += app_EndRequest;
        }

        void app_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Write("請求結束");
        }

        void app_BeginRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Write("請求開始");
        }
    }
}

在Init方法里面,通過HttpApplication對象來注冊請求的事件。這樣,每次發起一次http請求的時候都進到這兩個方法。

當然,這些注冊就能執行了嗎?想得美,系統哪里知道你這個自定義HttpModule的存在,所以必須要在Web.config里面聲明一下。

 <system.web>
    <httpModules>
        <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" />
    </httpModules>
  </system.web>

出現結果:

查閱資料后發現,原來IIS經典模式下必須要這樣配置:

<system.webServer>
    <modules>
        <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" preCondition="integratedMode" />
    </modules>
</system.webServer>

沒辦法,用微軟的東西就要遵守別人的游戲規則。改成這樣之后得到結果:

文中的“你好”來自這里:

 既然HttpModule是事件注冊機制的,那么如果需要在同一個事件里面去實現不同的功能,也就是說同一個事件注冊多次是否可行呢?我們來試一把:

假如TestMyModule.cs這個自定義Module的作用是功能檢查:

    public class TestMyModule:IHttpModule
    {
        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication app)
        {
            //事件注冊
            app.BeginRequest += app_BeginRequest;
            app.EndRequest += app_EndRequest;
        }

        void app_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Write("功能檢查結束");
        }

        void app_BeginRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Write("功能檢查開始");

            //功能檢查的處理邏輯...
        }
    }
TestMyModule.cs

然后新建一個TestMyModule2.cs這個自定義Module,去實現請求攔截的功能:

    public class TestMyModule2:IHttpModule
    {
        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication app)
        {
            //事件注冊
            app.BeginRequest += app_BeginRequest;
            app.EndRequest += app_EndRequest;
        }

        void app_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Write("請求攔截結束");
        }

        void app_BeginRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Write("請求攔截開始");

            //請求攔截的處理邏輯....
        }
    }
TestMyModule2.cs

最后在Web.config里面配置兩個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>

得到結果:

 

這說明同一個事件可以注冊多次,即可以在同一個事件里面做不同的事。

3、HttpModule和HttpHandler如何區分

通過上文的HttpModule的應用,我們看到在Init方法里面可以拿到當前應用的HttpApplication對象,拿到這個貌似就可以拿到當前請求上下文里面的Request、Response了,是不是就可以處理當前的http請求了,從這點上來說,HttpModule也能處理http請求,或者說具有處理http請求的能力。既然HttpHandler和HttpModule都可以處理http請求,那在使用的時候如何區分呢?上文說過,HttpModule的作用類似AOP,是針對某些通用功能(請求攔截、身份認證、檢查功能)的,而HttpHandler常用來處理某一類(ashx、aspx、asmx)http請求,兩者的側重點不同,至於具體在實際中如何使用,你可以自行考量。

4、UrlRoutingModule解析

好了,上面介紹那么多HttpModule的使用,都是在為了解Mvc里面的UrlRoutingModule做鋪墊。上文說過UrlRoutingModule的作用是攔截請求,那么它是如何做的呢,還是來反編譯看看吧。

public class UrlRoutingModule : IHttpModule
{
    // Fields
    private 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; }
}

重點肯定在Init()方法。

圖一:

注冊HttpApplication對象的PostResolveRequestCache事件。

圖二:

封裝HttpContext,成為HttpContextWrapper對象

圖三:

這部分代碼是我們上述路由理論的代碼實踐,所以這段代碼很重要,我們將代碼拷貝出來:

     public virtual void PostResolveRequestCache(HttpContextBase context)
        {
            //1.傳入當前上下文對象,得到與當前請求匹配的RouteData對象
            RouteData routeData = this.RouteCollection.GetRouteData(context);
            if (routeData != null)
            {
                //2.從RouteData對象里面得到當前的RouteHandler對象。其實這里的RouteHandler屬性對應就是一個MvcRouteHandler的對象。
                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.根據HttpContext和RouteData得到RequestContext對象
                    RequestContext requestContext = new RequestContext(context, routeData);
                    context.Request.RequestContext = requestContext;

                    //4.根據RequestContext對象得到處理當前請求的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.請求轉到HttpHandler進行處理(進入到ProcessRequest方法)。這一步很重要,由這一步開始,請求才由UrlRoutingModule轉到了MvcHandler里面
                        context.RemapHandler(httpHandler);
                }
            }
        }

博主在主要的地方加上了注釋。

代碼釋疑:這里有幾點需要說明的。

1、HttpApplication對象的PostResolveRequestCache事件在MSDN上的解釋是:在 ASP.NET 跳過當前事件處理程序的執行並允許緩存模塊滿足來自緩存的請求時發生。查閱相關資料發現,之所以在PostResolveRequestCache事件注冊路由、匹配HttpHandler,是為了滿足IIS6。可以參考Tom大叔的文章:http://www.cnblogs.com/TomXu/p/3756858.html

2、 IRouteHandler routeHandler = routeData.RouteHandler; 這里的routeHandler實際上是一個MvcRouteHandler類型的對象,為什么這么說,我們來反編譯下這個就會一目了然:

圖一:

 

MvcRouteHandler實現了IRouteHandler接口。然后我們重點來看GetHttpHandler()方法得到的是哪個HttpHandler。

圖二:

 

看到最后一句是不是立馬就明白了。也就是說GetHttpHandler()這個方法決定了采用MvcHandler去處理當前的http請求。所以在上述 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 這一句得到的就是一個MvcHandler的實例。

3、 context.RemapHandler(httpHandler); 這一句可以理解為將當前的請求上下文交給httpHandler這個對象去處理。

4、到這里,我們再反過來看前面的MVC的原理就完全明朗了。

  1. 請求被UrlRoutingModule部件攔截————通過注冊HttpApplication對象的PostResolveRequestCache事件來實現攔截
  2. 封裝請求上下文HttpContext,成為HttpContextWrapper對象。————將UrlRoutingModule的Init()方法轉到定義,可以看到這么一句: HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); 
  3. 根據當前的HttpContext,從Routes集合中得到與當前請求URL相符合的RouteData對象。————將UrlRoutingModule的Init()方法轉到定義,最終會找到PostResolveRequestCache()方法,方法里面有一句 RouteData routeData = this.RouteCollection.GetRouteData(context); 
  4. RouteDataHttpContext請求封裝成一個RequestContext對象。————同樣在上述方法里面 RequestContext requestContext = new RequestContext(context, routeData); 
  5. 根據RequestContext對象,從RouteData的RouteHandler中獲取IHttpHandler(MVC里面會有一個IHttpHandler的實現類MvcHandler)。————同樣在該方法里面 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 
  6. 執行IHttpHandler(MvcHandler)。———— context.RemapHandler(httpHandler); 將請求交給MvcHandler處理。
  7. 然后就是通過反射激活具體的controller,執行具體的action。————在MvcHandler的ProcessRequest()方法里面的執行邏輯

 四、總結

寫到這里,總算把整個過程梳理了一遍,很多細節都未涉及,但是大的過程應該還是明朗的。通篇比較偏理論,所以整體上比較枯燥,但是還是希望園友們能夠靜下心來慢慢看,因為博主覺得這些對於理解MVC原理太重要!!!想想看,如果你也完全理解了這個過程,是不是都可以自己通過實現IHttphandler和IHttpModule去搭建一個簡單的MVC框架了,不錯,博主確實是這樣打算的,這篇把理論搞清楚,下篇就是實現的細節了。其實寫自己的MVC框架更多的在於學習MVC原理,希望自己能夠堅持下去。如果你覺得本文能夠幫助你,可以右邊隨意 打賞 博主,也可以 推薦 進行精神鼓勵。你的支持是博主繼續堅持的不懈動力。

本文原創出處:http://www.cnblogs.com/landeanfen/

歡迎各位轉載,但是未經作者本人同意,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM