ASP.NET MVC的請求生命周期

来源:http://www.cnblogs.com/klsw/archive/2016/03/14/5277564.html
-Advertisement-
Play Games

我希望能理解在瀏覽器輸入URL並敲擊回車來請求一個ASP.NET MVC網站的頁面之後發生的任何事情。 為什麼需要關心這些?有兩個原因。首先是因為ASP.NET MVC是一個擴展性非常強的框架。例如,我們可以插入不同的ViewEngine來控制網站內容呈現的方式。我們還可以定義控制器生成和分配到某個


我希望能理解在瀏覽器輸入URL並敲擊回車來請求一個ASP.NET MVC網站的頁面之後發生的任何事情。

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

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

生命周期步驟概覽

當我們對ASP.NET MVC網站發出一個請求的時候,會發生5個主要步驟:

步驟1:創建RouteTable

當ASP.NET應用程式第一次啟動的時候才會發生第一步。RouteTable把URL映射到Handler。

步驟2:UrlRoutingModule攔截請求

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

步驟3:執行MvcHandler

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

步驟4:執行控制器

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

步驟5:調用RenderView方法

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

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

步驟1:創建RouteTable

當我們請求普通ASP.NET應用程式頁面的時候,對於每一個頁面請求都會在磁碟上有這樣一個頁面。例如,如果我們請求一個叫做SomePage.aspx的頁面,在WEB伺服器上就會有一個叫做SomePage.aspx的頁面。如果沒有的話,會得到一個錯誤。

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

因此,普通ASP.NET應用程式的工作方式佷簡單明瞭。我們請求頁面,頁面請求對應磁碟上的某個頁面,這個頁面執行ProcessRequest()方法並把內容發回瀏覽器。

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

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

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

http://MySite/Products/1
http://MySite/Products/2
http://MySite/Products/3

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

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

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

應用程式的路由表由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。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • LVS+Keepalived 介紹 LVS LVS是Linux Virtual Server的簡寫,意即Linux虛擬伺服器,是一個虛擬的伺服器集群系統。本項目在1998年5月由章文嵩博士成立,是中國國內最早出現的自由軟體項目之一。目前有三種IP負載均衡技術(VS/NAT、VS/TUN和VS/DR)
  • 負責維護公司產品的web伺服器搭建與維護,最近遇到一下狀況,今天在這裡簡單總結一下,希望對於剛剛一些剛入行的小伙伴有所幫助,避免再走彎路。 第一點:Tomcat記憶體設置: 一、常見的Java記憶體溢出有以下三種: 1. java.lang.OutOfMemoryError: Java heap spa
  •   線纜作為連接器件,相當於不同系統之間溝通的“橋梁”,選擇線纜類型的好壞,也決定著傳輸信號的質量,影響著整個系統的穩定性。 (1)特性阻抗   先說一下關於線纜在傳輸過程中的特性阻抗問題。   特性阻抗是指電纜無限長時該電纜所具有的阻抗,阻抗是阻止交流電流通的一種電阻,(所以萬用表測不能直接測出一
  •  
  •  
  • 最後要用一方法判斷ip地址是否正確,直接用.Net現成的類,方法如下:
  • 某一時候,為文本框(TextBox)裝飾個水印。它有兩種狀態,一是blur和focus。因此,我們可以在Javascript寫兩個事件: 演示:    
  • 【問】 在C#和Visual Basic的轉換中,以下一些轉換的用法和區別是什麼呢? [C#] [VB.NET] 【錯誤回答】 沒有區別,因為運行了之後都可以正常轉化。 【正解】 光從運行結果來看當然是毫無區別,因為題目所給出的僅僅是一部分的例子,不是全部。許多初學者容易產生“以偏概全”的錯誤認識。
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...