熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

詳解ASP.NET MVC的請求生命周期

2013-11-13 10:00:06  來源: .NET編程 

  本文的目的旨在詳細描述ASPNET MVC請求從開始到結束的每一個過程我希望能理解在浏覽器輸入URL並敲擊回車來請求一個ASPNET MVC網站的頁面之後發生的任何事情

  為什麼需要關心這些?有兩個原因首先是因為ASPNET MVC是一個擴展性非常強的框架例如我們可以插入不同的ViewEngine來控制網站內容呈現的方式我們還可以定義控制器生成和分配到某個請求的方式因為我想發掘任何ASPNET MVC頁面請求的擴展點所以我要來探究請求過程中的一些步驟

  其次如果你對測試驅動開發佷感興趣當為控制器寫單元測試時我們就必須理解控制器的依賴項在寫測試的時候我們需要使用諸如Typemock Isolator或Rhino Mocks的Mock框架來模擬某些對象如果不了解頁面請求生命周期就不能進行有效的模擬

  生命周期步驟概覽

  當我們對ASPNET MVC網站發出一個請求的時候會發生個主要步驟

  步驟創建RouteTable

  當ASPNET應用程序第一次啟動的時候才會發生第一步RouteTable把URL映射到Handler

  步驟UrlRoutingModule攔截請求

  第二步在我們發起請求的時候發生UrlRoutingModule攔截了每一個請求並且創建和執行合適的Handler

  步驟執行MvcHandler

  MvcHandler創建了控制器並且把控制器傳入ControllerContext然後執行控制器

  步驟執行控制器

  控制器檢測要執行的控制器方法構建參數列表並且執行方法

  步驟調用RenderView方法

  大多數情況下控制器方法調用RenderView()來把內容呈現回浏覽器ControllerRenderView()方法把這個工作委托給某個ViewEngine來做

  現在讓我們來詳細研究每一個步驟

  步驟創建RouteTable

  當我們請求普通ASPNET應用程序頁面的時候對於每一個頁面請求都會在磁盤上有這樣一個頁面例如如果我們請求一個叫做SomePageaspx的頁面在WEB服務器上就會有一個叫做SomePageaspx的頁面如果沒有的話會得到一個錯誤

  從技術角度說ASPNET頁面代表一個類並且不是普通類ASPNET頁面是一個Handler換句話說ASPNET頁面實現了IhttpHandler接口並且有一個ProcessRequest()方法用於在請求頁面的時候接受請求ProcessRequest()方法負責生成內容並把它發回浏覽器

  因此普通ASPNET應用程序的工作方式佷簡單明了我們請求頁面頁面請求對應磁盤上的某個頁面這個頁面執行ProcessRequest()方法並把內容發回浏覽器

  ASPNET MVC應用程序不是以這種方式工作的當我們請求一個ASPNET MVC應用程序的頁面時在磁盤上不存在對應請求的頁面而是請求被路由轉到一個叫做控制器的類上控制器負責生成內容並把它發回浏覽器

  當我們寫普通ASPNET應用程序的時候會創建很多頁面在URL和頁面之間總是一一對應進行映射每一個頁面請求對應相應的頁面

  相反當我們創建ASPNET MVC應用程序的時候創建的是一批控制器使用控制器的優勢是可以在URL和頁面之間可以有多對一的映射例如所有如下的URL都可以映射到相同的控制器上

  這些URL映射到一個控制器上,通過從URL中提取產品ID來顯示正確的產品。tW.WingwiT.COm這種控制器方式比傳統的ASP.NET方式更靈活。控制器方式可以產品更顯而易見的URL。

  那麼,某個頁面請求是怎麼路由到某個控制器上的呢?ASP.NET MVC應用程序有一個叫做路由表(Route Table)的東西。路由表映射某個URL到某個控制器上。

  一個應用程序有一個並且只會有一個路由表。路由表在Global.asax文件中創建。清單1包含了在使用Visual Studio新建ASP.NET MVC Web應用程序時默認的Global.asax文件。

  清單 1 – Global.asax



  

  using System;


  using System.Collections.Generic;


  using System.Linq;


  using System.Web;


  using System.Web.Mvc;


  using System.Web.Routing;



  namespace TestMVCArch


  {


  public class GlobalApplication : System.Web.HttpApplication


  {


  public static void RegisterRoutes(RouteCollection routes)


  {


  // Note: Change the URL to "{controller}.mvc/{action}/{id}" to enable


  // automatic support on IIS6 and IIS7 classic mode


  routes.Add(new Route("{controller}/{action}/{id}", new MvcRouteHandler())


  {


  Defaults = new RouteValueDictionary(new { action = "Index", id = "" }),


  });



  routes.Add(new Route("Default.aspx", new MvcRouteHandler())


  {


  Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),


  });


  }


  protected void Application_Start(object sender, EventArgs e)

  {

  RegisterRoutes(RouteTable.Routes);

  }

  }

  }

  應用程序的路由表由RouteTable.Routes的靜態屬性表示。這個屬性表示了路由對象的集合。在清單1列出的Global.asax文件中,我們在應用程序首次啟動時為路由表增加兩個路由對象(Application_Start()方法在第一次請求網站頁面的時候被調用一次)。

  路由對象負責把URL映射到Handler。在清單1中,我們創建了兩個路由對象。這2個對象都把URL映射到MvcRouteHandler。第一個路由映射任何符合{controller}/{action}/{id}模式的URL到MvcRouteHandler。第二個路由映射某個URL Default.aspx到MvcRouteHandler。

  順便說一下,這種新的路由構架可以脫離ASP.NET MVC獨立使用。Global.asax文件映射URL到MvcRouteHandler。然而,我們可以選擇把URL路由到不同類型的Handler上。這裡說的路由構架包含在一個叫做System.Web.Routing.dll的獨立程序集中。我們可以脫離MVC使用路由。

  步驟2:UrlRoutingModule攔截請求

  當我們對ASP.NET MVC應用程序發起請求的時候,請求會被UrlRoutingModule HTTP Module攔截。HTTP Module是特殊類型的類,它參與每一次頁面請求。例如,傳統ASP.NET包含了FormsAuthenticationModule HTTP Module用來使用表單驗證實現頁面訪問安全性。

  UrlRoutingModule攔截請求後做的第一件事情就是包裝當前的HttpContext為HttpContextWrapper2對象。HttpContextWrapper2類和派生自HttpContextBase的普通HttpContext類不同。創建的HttpContext的包裝可以使使用諸如Typemock Isolator或Rhino Mocks的Mock對象框進行模擬變得更簡單。

  接著,Module把包裝後的HttpContext傳給在之前步驟中創建的RouteTable。HttpContext包含了URL、表單參數、查詢字符串參數以及和當前請求關聯的cookie。如果在當前請求和路由表中的路由對象之間能找到匹配,就會返回路由對象。

  如果UrlRoutingModule成功獲取了RouteData對象,Module然後就會創建表示當前HttpContext和RouteData的RouteContext對象。Module然後實例化基於RouteTable的新HttpHandler,並且把RouteContext傳給Handler的構造函數。

  對於ASP.NET MVC應用程序,從RouteTable返回的Handler總是MvcHandler(MvcRouteHandler返回MvcHandler)。只要UrlRoutingModule匹配當前請求到路由表中的路由,就會實例化帶有當前RouteContext的MvcHandler。

  Module進行的最後一步就是把MvcHandler設置為當前的HTPP Handler。ASP.NET應用程序自動調用當前HTTP Handler的ProcessRequest()方法然後轉入下一步。

  步驟3:執行MvcHandler

  在之前的步驟中,表示某個RouteContext的MvcHandler被設置作為當前的HTTP Handler。ASP.NET應用程總是會發起一系列的事件,包括Star、BeginRequest、PostResolveRequestCache、 PostMapRequestHandler、PreRequestHandlerExecute和EndRequest事件(非常多的應用程序事件——對於完整列表,請查閱Visual Studio 2008文檔中的HttpApplication類)。

  之前內容中描述的所有東西都在PostResolveRequestCache和PostMapRequestHandler中發生。當前HTTP Handler的ProcessRequest()方法在PreRequestHandlerExecute事件之後被調用。

  當之前內容中創建的MvcHandler對象的ProcessRequest()被調用的時候,會創建一個新的控制器。控制器由ControllerFactory創建。由於我們可以創建自己的ControllerFactory,所以這又是一個可擴展點。默認的ControllerFactory名字相當合適,叫做DefaultControllerFactory。

  RequestContext以及控制器的名字被傳入ControllerFactory.CreateController()方法來獲得一個控制器。然後,從RequestContext和控制器構造ControllerContext對象。最後,調用控制器類的Execute()方法。在調用Execute()方法的時候會給方法傳入ControllerContext。

  步驟4:執行控制器

  Execute()方法首先創建TempData對象(在Ruby On Rails中叫做Flash對象)。TempData可以用於保存下次請求必須的臨時數據(TempData和會話狀態差不多,不長期占用內存)。

  接著,Execute()方法構建請求的參數列表。這些參數從請求參數中提取,將會被作為方法的參數。參數會被傳入執行的控制器方法。

  Execute()通過對控制器類進行反射來找到控制器的方法。控制器類是我們寫的。Execute()方法找到了我們控制器類中的方法後就執行它。Execute()方法不會執行被裝飾NonAction特性的方法。

  至此,就進入了自己應用程序的代碼。

  步驟5:調用RenderView方法

  通常,我們的控制器方法最後會調用RenderView()或RedirectToAction()方法。RenderView()方法負責把視圖(頁面)呈現給浏覽器。

  當我們調用控制器RenderView()方法的時候,調用會委托給當前ViewEngine的RenderView()方法。ViewEngine是另外一個擴展點。默認的ViewEngine是WebFormViewEngine。然而,我們可以使用諸如Nhaml的其它ViewEngine。

  WebForm的ViewEngine.RenderView()方法創建了一個叫做ViewLocator的類來尋找視圖。然後,它使用BuildManager來創建ViewPage類的實例。然後,如果頁面有ViewData就會設置ViewData。最後,ViewPage 的RenderView()方法被調用。

  ViewPage類從System.Web.UI.Page基類(和用於傳統ASP.NET的頁面一樣)派生。RenderView()方法做的最後一個工作就是調用頁面類的ProcessRequest()。調用視圖的ProcessRequest()生成內容的方式和普通ASP.NET頁面生成內容的方式一致。

  可擴展點

  ASP.NET MVC生命周期在設計的時候包含了很多可擴展點。我們可以自定義通過插入自定義類或覆蓋既有類來自定義框架的行為。下面是這些擴展點的概要:

  路由對象:當我們創建路由表的時候,調用RouteCollection.Add()方法來增加新的路由對象。Add()方法接受了RouteBase對象。我們可以通過派生RouteBase基類來實現自己的路由對象。

  MvcRouteHandler :當創建MVC應用程序的時候,我們把URL映射到MvcRouteHandler對象上。然而,我們可以把URL映射到實現IRouteHandler接口的任何類上。路由類的構造函數接受任何實現IRouteHandler接口的對象。

  MvcRouteHandler.GetHttpHandler() : MvcRouteHandler 類的GetHttpHandler()方法是virtual方法。默認情況下,MvcRouteHandler返回MvcHandler。如果願意的話,我們可以覆蓋GetHttpHandler()方法來返回不同的Handler。

  ControllerFactory :我們可以通過System.Web.MVC.ControllerBuilder.Current.SetControllerFactory()方法指定一個自定義類來創建自定義的控制器工廠。控制器工廠負責為某個控制器名和RequestContext返回控制器。

  控制器:我們可以通過實現Icontroller接口來實現自定義控制器。這個接口只有一個Execute(ControllerContext controllerContext)方法。

  ViewEngine:我們可以為控制器指定自定義的ViewEngine。通過為公共的Controller.ViewEngine屬性指定ViewEngine來把ViewEngine指定給控制器。ViewEngine必須實現IviewEngine接口,接口只有一個方法:RenderView(ViewContext viewContext)。

  ViewLocator :ViewLocator把視圖名映射到實際視圖文件上。我們可以通過WebFormViewEngine.ViewLocator的屬性來執行自定義的ViewLocator。


From:http://tw.wingwit.com/Article/program/net/201311/12256.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.