asp.net MVC 應用程序的生命周期


  首先我們知道http是一種無狀態的請求,他的生命周期就是從客戶端瀏覽器發出請求開始,到得到響應結束。那么MVC應用程序從發出請求到獲得響應,都做了些什么呢?

      本文我們會詳細討論MVC應用程序一個請求的生命周期,從一個控件到另一個控件是怎樣被處理的。我們還會詳細介紹一下整個請求的生命周期中,用到的相關組件。因為在平常的開發過程中,我們可能知道怎樣去使用MVC框架來處理相關的請求,大部分的時候我們只是在controller和action方法之間做相關的處理,對於真正內在的運行機制可能不是很了解。其實當我們對內在機制有了一定的了解以后,會發現微軟的MVC框架的擴展性很強,到處都留有擴展接口,讓我們通過擴展能夠自己定義自己所需要的處理機制,這也正是為什么MVC框架如此出名的原因。

  當我最開始學習使用mvc的時候,困擾我的一個問題就是,一個請求的流程控制是怎樣的呢?從view到controller再到action之間經歷了什么?那個時候我還不清楚HTTP module和HTTP  handler在處理一個請求中扮演什么樣的角色,起什么樣的作用呢。畢竟MVC是一個web開發框架,在整個請求處理過程中,肯定包含了http module和http handler。其實還有很多相關的組件包含在一個完整的mvc應用程序請求生命周期里,在整個請求過程中他們都扮演者非常重要的角色。盡管大部分時候我們都使用的是框架提供的默認的函數,但是如果我們了解了每個控件所扮演的角色,我們就可以輕松的擴展和使用我們自己實現的方法,就目前來說MVC是擴展性比較強的框架。下面是本章節的主要內容:

  • HttpApplication
  • HttpModule
  • HttpHandler
  • ASP.NET MVC運行機制
  • UrlRoutingModule
  • RouteHandler
  • MvcHandler
  • ControllerFactory
  • Controller
  • ActionInvoker
  • ActionResult
  • ViewEngine

HttpApplication                                                    

   我們都知道,在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 類的一個實例來處理其生存期中收到的眾多請求。但是,它每次只能處理一個請求。這樣,成員變量才可用於存儲針對每個請求的數據。

意思就是說ASP.NET應用程序,不管是MVC還是WebForm,最終都會到達一個HttpApplication類的實例。HttpApplication是整個ASP.NET基礎架構的核心,負責處理分發給他的請求。HttpApplication處理請求的周期是一個復雜的過程,在整個過程中,不同階段會觸發相映的事件。我們可以注冊相應的事件,將處理邏輯注入到HttpApplication處理請求的某個階段。在HttpApplication這個類中定義了19個事件來處理到達HttpApplication實例的請求。就是說不管MVC還是WebForm,最終都要經過這19個事件的處理,那么除了剛才說的MVC和WebFrom在請求處理機制上大部分都是相同的,不同之處在哪呢?他們是從哪里開始分道揚鑣的呢?我們猜想肯定就在這19個方法中。我們繼續往下看。

我們來看看這19個事件:

應用程序按照以下順序執行由 global.asax 文件中定義的模塊或用戶代碼處理的事件:

事件名稱:

簡單描述:

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事件。

HttpModule                                                           

 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對象執行到某個事件的時候自然就會出發。

HttpHandler                                                           

對於不同的資源類型的請求,ASP.NET會加載不同的HttpHandler來處理。所有的HttpHandler都實現了IhttpHandler接口。

public interface IHttpHandler
{
    // Methods
    void ProcessRequest(HttpContext context);

    // Properties
    bool IsReusable { get; }
}

 

我們看到該接口有一個方法ProcessRequest,顧名思義這個方法就是主要用來處理請求的。所以說每一個請求最終分發到自己相應的HttpHandler來處理該請求。

ASP.NET MVC 運行機制                                      

好了,上面說了那么多,其實都是給這里做鋪墊呢。終於到正題了。先看看下面這張圖,描述了MVC的主要經歷的管道事件:

  上圖就是一個完整的mvc應用程序的一個http請求到響應的整個兒所經歷的流程。從UrlRoutingModule攔截請求到最終ActionResult執行ExecuteResult方法生成響應。

下面我們就來詳細講解一下這些過程都做了些什么。

 

UrlRoutingModule                                                 

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。

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                                                           

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:

ControllerFactory                                                  

主要是用來生成Controller對象

ControllerFactory實現了接口IControllerFactory.

Controller                                                                                

這里我們大概就知道了,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                                                       

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。

 

 ActionResult                                                         

到目前為止,我們看到了Action方法被ActionInvoker調用。所有的Action方法有一個特性,就是返回一個ActionResult類型的數據。

public abstract class ActionResult
  {
      public abstract void ExecuteResult(ControllerContext context);
  }

ExecuteResult()是一個抽象方法,所以不同的子類可以提供不同的ExecuteResult()實現。

ActionResult執行后響應輸出到客戶端。

 

ViewEngine                                                           

ViewResult幾乎是大部分應用程序的返回類型,主要通過ViewEngine引擎來展示view的。ViewEngine可能主要就是生成Html元素的引擎。Framwork提供了2種引擎,Razor View Engine 和Web Form View Engine.如果你想自定義引擎,你可以創建一個引擎只要實現IViewEngine接口即可。

IViewEngine 有下面幾個方法:

1、FindPartialView :當controller需要返回一個PartialView的時候,FindPartialView方法 就會被調用。

2、FindView 

3、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輸出響應。

總結:如果我們理解了整個過程中發生了什么,哪些類和哪些方法被調用,我們就可以在需要擴展的地方輕松的進行擴展。

 


免責聲明!

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



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