ASP.NET是一種建立動態Web應用程序的技術。它是.NET框架的一部分,可以使用任何.NET兼容的語言編寫ASP.NET應用程序。相對於Java、PHP等,ASP.NET具有方便性、靈活性、性能優、生產效率高、安全性高、完整性強及面向對象等特性,是目前主流的網絡編程技術之一.它可以讓開發者快速高效的創建應用程序而不必關注Http,Html,Javascript等底層的詳細信息。隨着越來越多的企業將網站升級為APS.NET,web應用程序的復雜性不斷增加,即使當初MS推出ASP.NET的時候,如何強調codebehind的優勢,但是它在架構上回避不了一個問題:如果全部codebehind的話,.cs勢必需要擔負起同時處理事務邏輯和視圖各方面屬性的任務。一個是對邏輯的控制,另一個是對視圖(服務器控件)的控制。 當預期超過了一定的閾值可能使的Web Forms不再是一種最佳的選擇。由於它犧牲掉一個優秀模型的某些方面,比如可維護性,可讀性,可測試性以及對HTMl的可控性,越來越多的人認為ASP.NET Web Forms model不太理想。
ASP.NET MVC是創建APS.NET應用的一種新的平台,和傳統的 Web Forms一樣運行於ASP.NET run-time environment。MVC專注於用戶瀏覽頁面時的行為,並且具有不同的試圖引擎來控制生成的標記。在標准的ASP.NET runtime environment之上,MVC創建了自身的shell,該shell一端與ASP.NET run-time objects(例如:request,HttpContext)相連,另一端將它本身對象集暴露給內部的組件,非常有意思的是對象集是被注入到ASP.NET MVC runtime shell中的。因此它具有很高的可測試性。MVC是怎么樣處理請求的呢,再這之前,我們先了解下ASP.NET的運行機制。
I:The ASP.NET Runtime 機制
從開發者的角度來看,Web Forms和MVC是兩種不同的開發框架,但是它們具有很多共同點。特別是它們運行於相同的運行時環境:the standard ASP.NET runtime environment.
通常來講,運行時環境就是一組托管在Web服務器上的組件,用來處理到達服務器的Request並向客戶端返回Response。ASP.NET Web Forms and ASP.NET MVC運行時機制是相同的,這就意味着ASP.NET pages and ASP.NET MVC可以同時存在於一個應用程序中。盡管底層機制是相同的,但是ASP.NET MVC and a Web Forms處理request的步驟卻完全不同。MVC在ASP.NET runtime之上有自己的run-time shell並且實現自己的管線來處理它獲得的請求。
我們通過創建虛擬目錄將資源Host到IIS下,原則上,我們可以通過IIS訪問置於虛擬目錄下的所有Resource,這部僅僅包含一些靜態資源文件,比如圖片、純Html文件、CSS、JS等等,也包含一些需要動態執行的文件,比如aspx,asmx等等,我們還可以將Remoting和WCF Service Host到IIS下。對於這些靜態的文件,IIS直接提取對應的文件將其作為Http Response返回給Client,但是對於這些需要進一步處理的動態執行的文件,IIS必須將Request進一步傳遞給對應的處理程序,待處理程序執行完畢獲得最終的Http Response通過IIS返回給Client。對於IIS來說,這些處理程序通過ISAPI Extension來體現。對於基於ASP.NET的Resource,其對應的ISAPI Extension為ASP.NET ISAPI,通過一個aspnet_isapi.dll承載。IIS的Metadata database維護着一個稱為ISAPI Extension Mapping的數據表,負責將不同類型的Resource影射到對應的ISAPI Extension.
IIS5.x
在IIS5.x中,web服務和ASP.NET應用是分離開來的。IIS5.x運行在一個inetinfo.exe的進程中,InetInfo.exe是一個Native Executive,並非一個托管的程序。IIS分析Request的目標資源文件的擴展名(這里是aspx),通過ISAPI Extension Mapping獲知對應的ISPAI為ASP.NET ISAPI,於是加載aspnet_isapi.dll。到此為止,該Request的處理交由ASP.NET ISAPI處理。ASP.NET ISAPI會創建一個叫做aspnet_wp.exe的Worker Process(如果該進程不存在的話),在aspnet_wp.exe初始化的時候會加載CLR,從而為ASP.NET Application創建一個托管的運行環境,在CLR初始化的使用會加載兩個重要的dll:AppManagerAppDomainFactory和ISAPIRuntime。通過AppManagerAppDomainFactory的Create方法為Application創建一個Application Domain;通過ISAPIRuntime的ProcessRequest處理Request,進而將流程拖入到ASP.NET Http Runtime Pipeline的范疇,ASP.NET Http Runtime Pipeline對Http Request的處理是一個相對復雜的過程,相關的介紹會放在本篇文章的下一部份。在這里我們可以把它看成是一個黑盒,它接管Request,最終生成Html。
當IIS5.x運行的進程和asp.net運行的進程相互獨立時,它們之間采用了Named Pipe來進行通訊。IIS5.x主要應用在Windows XP或Windows 2000操作系統中。
-
- 1. 首先,同一台主機上再同一時間只能運行一個aspnet_wp進程,每個基於虛擬目錄的ASP.NET Application對應一個Application Domain,也就是說每個Application都運行在同一個Worker Process中,Application之間的隔離是基於Application Domain的,而不是基於Process的。
- 2. 其次,ASP.NET ISAPI不但負責創建aspnet_wp Worker Process,而且負責監控該進程,如果檢測到aspnet_wp的Performance降低到某個設定的下限,ASP.NET ISAPI會負責結束掉該進程。當aspnet_wp結束掉之后,后續的Request會導致ASP.NET ISAPI重新創建新的aspnet_wp Worker Process。
- 3. 最后,由於IIS和Application運行在他們各自的進程中,他們之間的通信必須采用特定的通信機制。本質上IIS所在的InetInfo進程和Worker Process之間的通信是同一台機器不同進程的通信(local interprocess communications),處於Performance的考慮,他們之間采用基於Named pipe的通信機制。ASP.NET ISAPI和Worker Process之間的通信通過他們之間的一組Pipe實現。同樣處於Performance的原因,ASP.NET ISAPI通過異步的方式將Request 傳到Worker Process並獲得Response,但是Worker Process則是通過同步的方式向ASP.NET ISAPI獲得一些基於Server的變量。
IIS6.0
-
- IIS6.0引入了應用程序池(Application Pool)的概念。IIS6.0先通過組件http.sys來接收客戶端的請求,http.sys接收到一個基於aspx的http request,然后它會根據IIS中的Metabase查看該基於該Request的Application屬於哪個Application Pool,如果該Application Pool不存在,則創建之。否則直接將request發到對應Application Pool的Queue中。我上面已經說了,每個Application Pool對應着一個Worker Process:w3wp.exe,毫無疑問他是運行在User Mode下的。在IIS Metabase中維護着Application Pool和worker process的Mapping。WAS(Web Administrative service)根據這樣一個mapping,將存在於某個Application Pool Queue的request 傳遞到對應的worker process(如果沒有,就創建這樣一個進程)。在worker process初始化的時候,加載ASP.NET ISAPI,ASP.NET ISAPI進而加載CLR。最后的流程就和IIS 5.x一樣了:通過AppManagerAppDomainFactory的Create方法為Application創建一個Application Domain;通過ISAPIRuntime的ProcessRequest處理Request,進而將流程進入到ASP.NET Http Runtime Pipeline。
- 每個應用程序池對應着一個w3wp.exe的工作進程。在IIS Metabase 中維護着應用程序池和工作進程的對照。WAS(Web Administrative service)根據這樣一個對照,將存在於某個應用程序池隊列的客戶端請求傳遞到對應的工作進程(如果沒有,就創建這樣一個進程)。在工作進程初始化的時候,加載ASP.NET ISAPI,ASP.NET ISAPI 進而加載CLR。最后的流程就和IIS 5.x一樣了:通過AppManagerAppDomainFactory的Create方法為Application創建一個Application Domain;通過ISAPIRuntime的ProcessRequest處理Request,進而將流程進入到ASP.NET Http Runtime Pipeline(Pipeline是接下來要關注的重點)。
- IIS6.0主要應用在Windows 2003 server操作系統中。
IIS7.0
-
- IIS7.0完全整合了.NET。ASP.NET從ISAPI Extension的角色進入了IIS的核心。IIS7.0處理請求的流程如下:
- 當客戶端瀏覽器開始HTTP請求一個WEB服務器的資源時,HTTP.sys 攔截到這個請求。
- HTTP.sys聯系WAS向配置存儲中心獲取信息。
- WAS 向配置存儲中心請求配置信息。applicationHost.config。
- WWW 服務接受到配置信息,配置信息指類似應用程序池配置信息,站點配置信息等等。
- WWW 服務使用配置信息去配置 HTTP.sys 處理策略。
- WAS為請求的應用程序池啟動一個工作進程。
- 工作進程處理請求,並把回復信息返回給HTTP.sys。
- 客戶端接受到處理結果信息。
II:HTTP運行期
HTTP運行期處理客戶端應用程序(例如Web瀏覽器)進入的一個Web請求,通過處理它的應用程序的適當組件路由請求,然后產生響應並發回提出請求的客戶端應用程序。
進入的HTTP Web請求最先由IIS Web服務器接收到,它在此請求基於ASP.NET已注冊處理的擴展名傳送到ASP.NET ISAPI上。
HTTP運行期首先創建一個HttpContext對象的實例,它包含了當前正在處理的請求信息,接着創建在處理邏輯中涉及到的所有其他組件都可以使用的上下文對象。HttpContext實例提供了對請求對象(HttpRequest類的實例)和響應對象(HttpResponse類的實例)的訪問。
HttpContext
HttpContext體現當前Request的上下文信息,它的生命周期知道整個Request處理結束或者處理超時。通過HttpContext對象我們可以訪問屬於當前Request的一系列常用的對象:Server,Session,Cache,Application,Request,Response,Trace,User,Profile等等。此外我們可以認為將一些數據放在Items屬性中作為狀態管理的一種方式,不過這種狀態管理和其他一些常用的方式,比如Session,Cache,Application,Cookie等,具有根本性的不同之處是其生命周期僅僅維持在當前Request的Context中。
HttpApplication
就像其名稱體現的一樣,HttpApplication基本上可以看成是真個ASP.NET Application的體現。HttpApplication和置於虛擬根目錄的Gloabal.asax對應。通過HttpApplicationFactory.GetApplicationInstance創建一個基於Gloabal.asax的HttpApplication對象。在HttpApplicationFactory.GetApplicationInstance方法返回創建的HttpApplication對象之前,會調用一個名為InitInternal的內部方法,該方法會做一些列的初始化的操作,在這些初始化操作中,最典型的一個初始化方法為InitModules(),該方法的主要的目的就是查看Config中注冊的所有HttpModule,並根據配置信息加載相應的Assembly,通過Reflection創建對應的HttpModule,並將這些Module加到HttpApplication 的_moduleCollection Filed中。
HttpApplication本身並包含對Request的任何處理,他的工作方式是通過在不同階段出發不同Event來調用我們注冊的Event Hander。
HttpModule
我們上面提到HttpApplication就是一個ASP.NET Application的體現,HttpApplication本身並不提供對Request的處理功能,而是通過在不同階段出發不同的Event。我們能做的只能是根據我們具體的需求將我們的功能代碼作為Event Handler注冊到需要的HttpApplication Event上面。注冊這些Event Handler,我們首先想到的肯定就直接在HttpApplication對應的Global.asax中定義我們的EventHandler好了。這是最直接的辦法,而且Global.asax提供一個簡潔的方式是我們的實現顯得簡單:不需要向一般注冊Event一樣將Delegate添加到對應的Event上面,而是直接通過方法名稱和對應的Event匹配的方式直接將對應的方法作為相關的Event Handler。比如Application_ AcquireRequestState就是AcquireRequestState Event handler。
但是這種方式在很多情況下卻達不到我們的要求,更多地,我們需要的是一種Plug-in的實現方式:我們在外部定義一些Request Processing的功能,需要直接運用到我們的Application之中。通過使用HttpModule封裝這些功能模塊,並將其注冊到我們的Application的發式可以很簡單的實現這種功能。更多內容參考:http://www.cnblogs.com/jyan/articles/2563794.html
HttpHandler
如果說HttpModule關注的是所有Inbound Request的處理的話,Handler確實關注基於某種類型的ASP.NET Resource的Request。比如一個.apsx的Web Page通過一個System.Web.UI.Page來處理。HttpHandler和他所處理的Resource通過Config中的system.web/handlers section來定義.需要注意的是,我們不但可以單純地定義一個實現了System.Web.IHttpHandler的Type,也可以定義一個實現了System.Web.IHttpHandlerFactory 的Type。System.Web.UI.Page是一個典型的Httphandler,相信對此大家已經很熟悉了。在最后還說說另一個典型的HttpHandler:System.Web.HttpForbiddenHandler,從名稱我們不難看出,它用於那些禁止訪問的Resource,現在應該知道了為了Global.asax不同通過IIS訪問了吧.
更多內容參考:http://www.cnblogs.com/jyan/articles/2563801.html
III:MVC請求過程
圖中可以初步看出一個HttpRequest是如何被ASP.NET和ASP.NET MVC框架執行的:經過IIS和ASP.NET處理后,Core Routing會首先根據URL匹配物理路徑上的文件,如果不能匹配則由核心路由模塊執行路由,路由被匹配后,MvcRouteHandler會將這個請求“帶入”MVC框架,執行Controller和Action,Action可以直接注入response,或者更平常的是返回一個ActionResult,ActionResult的ExecutedResult方法將被調用,如果是個ViewResult(繼承自ActionResult),則會使用WebFormViewEngine轉化一個Html,並注入到response。
下圖是Web Forms與MVC中運行時環境,可以看出,MVC運行時環境是原始ASP.NET運行時環境的定制版本。
[參考]http://www.cnblogs.com/artech/archive/2007/09/13/891266.html