1. 程式人生 > >MVC系列——MVC原始碼學習:打造自己的MVC框架(一:核心原理)

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

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

MVC原始碼學習系列文章目錄:

一、MVC原理解析

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

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

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原理,希望自己能夠堅持下去。如果你覺得本文能夠幫助你,可以右邊隨意 打賞 博主,也可以 推薦 進行精神鼓勵。你的支援是博主繼續堅持的不懈動力。

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

相關推薦

MVC系列——MVC原始碼學習打造自己MVC框架核心原理

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

Asp.net MVC 搭建屬於自己框架

C4D pagedlist del tran 6.0 ext 才有 應該 frame 網址:https://www.cnblogs.com/sggx/p/4555255.html 為什麽要自己搭框架?   大家夥別急,讓我慢慢地告訴你!大家有沒有這種感覺,從一家跳槽到另一家

資料結構篇圖的遍歷深度優先遍歷

深度優先遍歷,也稱作深度優先搜尋,縮寫為DFS 深度優先遍歷從某個頂點出發,訪問此頂點,然後從v的未被訪問的鄰接點觸發深度優先便利圖,直至所有和v有路徑想通的頂點都被訪問到。 這樣我們一定就訪問到所有結點了嗎,沒有,可能還有的分支我們沒有訪問到,所以需要回溯(一般情況下都設定一個數組,來

MVC系列——MVC原始碼學習打造自己MVC框架瞭解神奇的檢視引擎

前言:通過之前的三篇介紹,我們基本上完成了從請求發出到路由匹配、再到控制器的啟用,再到Action的執行這些個過程。今天還是趁熱打鐵,將我們的View也來完善下,也讓整個系列相對完整,博主不希望爛尾。對於這個系列,通過學習原始碼,博主也學到了很多東西,在此還是把博主知道的先發出來,供大家參考。 MVC原

MVC系列——MVC原始碼學習打造自己MVC框架原始碼

前言:上篇介紹了下 MVC5 的核心原理,整篇文章比較偏理論,所以相對比較枯燥。今天就來根據上篇的理論一步一步進行實踐,通過自己寫的一個簡易MVC框架逐步理解,相信通過這一篇的實踐,你會對MVC有一個更加清晰的認識。 MVC原始碼學習系列文章目錄: 這篇博主打算從零開始一步一步來加上MVC裡面用到

MVC系列——MVC原始碼學習打造自己MVC框架自定義路由規則

前言:上篇介紹了下自己的MVC框架前兩個版本,經過兩天的整理,版本三基本已經完成,今天還是發出來供大家參考和學習。雖然微軟的Routing功能已經非常強大,完全沒有必要再“重複造輪子”了,但博主還是覺得自己動手寫一遍印象要深刻許多,希望想深入學習MVC的童鞋自己動手寫寫。好了,廢話就此打住。 MVC原始

mvc原理打造自己MVC框架1.0

一、MVC的原理 從請求到服務端接受到請求中間這個過程經歷了哪些步驟: 第一步:請求被UrlRoutingModule部件攔截 第二步:封裝請求上下文HttpContext,成為HttpContextWrapper 第三步:根據當前的HttpContext,從Routes集合中得到與

caffe學習系列訓練自己的圖片集超詳細教程

    學習的caffe的目的,不是簡單的做幾個練習,而是最終落實到自己的專案或科研中去。因此,本文介紹一下,從自己的原始圖片到lmdb資料,再到訓練和測試的整個流程。 一、資料的準備     有條件的同學,可以去ImageNet的官網點選開啟連結,下載ImageNet圖片

寫一個屬於自己的PHP的MVC框架

最近想做個PHP的個人部落格作為學習用,但是發現儘管把PHP函式用得很熟悉了,按照常規的辦法,寫一個頁面處理一個請求,僅僅一個部落格就可能有很多個頁面,而且php程式碼和html程式碼都結合的非常緊密,如果想要實現更換面板的功能,就顯得非常無力。在網上找了好多framework框架,但似乎又要開始學

吳恩達深度學習系列課程筆記卷積神經網路

本系列文章將對吳恩達在網易公開課“深度學習工程師”微專業內容進行筆記總結,這一部分介紹的是“卷積神經網路”部分。 1、計算機視覺 計算機視覺在我們還是生活中有非常廣泛的應用,以下幾個是最常見的例子: 影象分類: 可以對影象中的物體種類進行判斷,如確定影象中

逆向工程第002篇打造自己的仙劍奇俠

        眾所周知,在國產RPG遊戲裡面,《仙劍奇俠傳》是永恆的經典。釋出近二十年以來,依舊話題不斷。但是鮮有人對其進行逆向分析,只是聽說多年之前曾有人為了探究其是否有隱藏劇情,從而採取了某種逆

菜鳥系列Fabric原始碼學習 — 區塊同步

Fabric 1.4 原始碼分析 區塊同步 本文主要從原始碼層面介紹fabric peer同步區塊過程,peer同步區塊主要有2個過程: 1)peer組織的leader與orderer同步區塊 2)peer組織間peer同步區塊。 1. peer leader和orderer同步區塊 首先,orderer對外

菜鳥系列Fabric原始碼學習 — committer記賬節點

Fabric 1.4 原始碼分析 committer記賬節點 本文件主要介紹committer記賬節點如何初始化的以及committer記賬節點的功能及其實現。 1. 簡介 記賬節點負責驗證交易和提交賬本,包括公有資料(即區塊資料,包括公共資料和私密資料hash值)與私密資料。在提交賬本前需要驗證交易資料的有

菜鳥系列Fabric原始碼學習 — MVCC驗證

Fabric 1.4 原始碼分析 MVCC驗證 讀本節文件之前建議先檢視[Fabric 1.4 原始碼分析 committer記賬節點]章節。 1. MVCC簡介 Multi-Version Concurrency Control 多版本併發控制,MVCC 是一種併發控制的方法,一般在資料庫管理系統中,實現對

【SSH之旅】步步學習Hibernate框架關於持久化

stc localhost 對象 schema hbm.xml java let pass [] 在不引用不論什麽框架下,我們會通過平庸的代碼不停的對數據庫進行操作,產生了非常多冗余的可是又有規律的底層代碼,這樣頻繁的操作數據庫和大量的底層代碼的反復

Java總結篇系列Java多線程

常見 而是 同時 private 狀態 過程 運行時 不同的 bstr Java總結篇系列:Java多線程(一) 多線程作為Java中很重要的一個知識點,在此還是有必要總結一下的。 一.線程的生命周期及五種基本狀態 關於Java中線程的生命周期,首先看一下下面這張較

機器學習之支持向量機支持向量機的公式推導

根據 監督式 art 通用 利用 哪些 這就是 在線 方法 註:關於支持向量機系列文章是借鑒大神的神作,加以自己的理解寫成的;若對原作者有損請告知,我會及時處理。轉載請標明來源。 序: 我在支持向量機系列中主要講支持向量機的公式推導,第一部分講到推出拉格朗日對偶函數的對偶因

機器學習系列(12)_XGBoost參數調優完全指南附Python代碼

row cti tar libraries 叠代 數據科學家 值範圍 邏輯回歸 doc https://blog.csdn.net/han_xiaoyang/article/details/52665396 轉: 原文地址:Complete Guide to Paramet

SpringCloud系列Eureka 服務發現框架定義 Eureka 服務端、Eureka 服務信息、Eureka 發現管理、Eureka 安全配置、Eureka-HA(高可用) 機制、Eureka 服務打包部署

pac elf figure 傳遞 uri rect body 情況 服務組 1、概念:Eureka 服務發現框架 2、具體內容 對於服務發現框架可以簡單的理解為服務的註冊以及使用操作步驟,例如:在 ZooKeeper 組件,這個組件裏面已經明確的描述了一個服務的註冊以及發

python程式設計從入門到實踐學習筆記-Django開發使用者賬戶

讓使用者能夠輸入資料(表單) 在建立使用者賬戶身份驗證系統之前,先新增幾個頁面,讓使用者能偶輸入資料。新增新主題、新增新條目以及編輯既有條目。 新增新主題 1.用於新增主題的表單 建立一個forms.py檔案與models.py放在同一目錄下。 from django import