小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

從底層角度看ASP.NET-A low-level Look at the ASP.NET...

 kittywei 2011-04-29
從更低的角度
這篇文章在一個(gè)底層的角度來(lái)關(guān)注一個(gè)web請(qǐng)求怎樣到達(dá)asp.net框架,從web服務(wù)器,通過(guò)ISAPI??纯催@些后面發(fā)生了什么,讓我們停止對(duì)asp.net的黑箱猜想。
ASP.NET是一個(gè)非常強(qiáng)大用來(lái)創(chuàng)建web應(yīng)用程序的平臺(tái),它為創(chuàng)建web應(yīng)用程序提供了大量的靈活強(qiáng)大的支持。大多數(shù)人僅僅熟悉表層的WebForm和webservice,他們位于整個(gè)ASP.NET架構(gòu)的最表層。在這篇文章里,我將會(huì)描述非常底層的ASP.NET并且解釋一個(gè)web請(qǐng)求是如何從web服務(wù)器到達(dá)ASP.NET運(yùn)行時(shí),并且通過(guò)ASP.NET管道(pipeline)處理請(qǐng)求的。
對(duì)我來(lái)說(shuō)理解一個(gè)平臺(tái)的內(nèi)部機(jī)制,能夠然我自己得到相應(yīng)的滿意和舒適,同時(shí)也可以幫助我們寫(xiě)出更好的應(yīng)用程序。了解這些工具是怎樣作為整個(gè)框架的某一部分而互相配合,并且更加容易的找到問(wèn)題的解決方案,更加重要的,在發(fā)生錯(cuò)誤的時(shí)候,能夠幫助你定位以及調(diào)試這個(gè)錯(cuò)誤。這篇文章的目標(biāo)就是從系統(tǒng)的角度來(lái)看ASP.NET,并且?guī)椭斫猓?qǐng)求是如何到達(dá)ASP.NET處理管道的。就這點(diǎn)而言,我們將會(huì)看到核心引擎,以及一個(gè)web請(qǐng)求是如何終結(jié)的。很多東西都是你在日常的工作中不需要知道的,但是它有利于理解ASP.NET架構(gòu)怎樣路由到你經(jīng)常編寫(xiě)的應(yīng)用程序的高層代碼的。
大多數(shù)使用ASP.NET的人都是對(duì)Wenforms和WebService熟悉。這些高層的實(shí)現(xiàn)能夠簡(jiǎn)化創(chuàng)建以web為基礎(chǔ)的應(yīng)用程序,并且,ASP.NET是一個(gè)驅(qū)動(dòng)引擎,提供了對(duì)web服務(wù)器的底層接口,也為你在你的程序中用到的典型的高層前端服務(wù)的路由機(jī)制提供接口。WebForm和WebService僅僅是兩個(gè)在ASP.NET框架核心上構(gòu)建的非常經(jīng)久耐用的HttpHandlers。
然而,ASP.NET在低層提供了更多的靈活性。HTTP Runtime和請(qǐng)求管道提供了所有的相同創(chuàng)建WebForm和WebService的能力,它們實(shí)際上也是由.NET托管代碼實(shí)現(xiàn)。而且,你如果決定自己定制、創(chuàng)建一個(gè)比WebForm稍低層的平臺(tái),所有的ASP.NET的低層的這些功能、機(jī)制,你也同樣可以使用。
WebForm顯然是創(chuàng)建大多數(shù)web應(yīng)用程序的最容易的方式,但是你在創(chuàng)建自定義內(nèi)容的handlers或者對(duì)于進(jìn)、出內(nèi)容需要特殊的處理,或者你需要為另外一個(gè)應(yīng)用程序創(chuàng)建一個(gè)定制的應(yīng)用程序接口,使用低層的handlers或者modules能夠給你更好的性能以及對(duì)一個(gè)web請(qǐng)求的更好的控制。你也可以繞過(guò)WebForm和WebService的這些高層實(shí)現(xiàn)提供的這些功能、機(jī)制直接在底層進(jìn)行操作。

什么是ASP.NET

讓我們以一個(gè)簡(jiǎn)單的定義開(kāi)始:什么事ASP.NET?我喜歡把ASP.NET定義如下:
ASP.NET是一個(gè)使用托管代碼完成的,從前到后處理web請(qǐng)求的,久經(jīng)考驗(yàn)的框架。它并不僅僅是WebForm和WebService…
ASP.NET是一個(gè)請(qǐng)求處理引擎,它通過(guò)它的內(nèi)部的管道將一個(gè)請(qǐng)求傳送到一個(gè)開(kāi)發(fā)者的代碼上。實(shí)際上這個(gè)引擎完全獨(dú)立于HTTP或者web服務(wù)器。事實(shí)上,HTTP Runtime是一個(gè)在IIS或者其他任何服務(wù)器之外的,您的應(yīng)用程序的宿主環(huán)境。舉一個(gè)例子,您可以將ASP.NET runtime放到一個(gè)Windows窗口中(點(diǎn)擊獲得更多詳情http://www./presentations/ASP.NETruntime/ASP.NETruntime.asp
運(yùn)行時(shí)為請(qǐng)求通過(guò)這個(gè)管道提供了一個(gè)復(fù)雜而又優(yōu)雅的機(jī)制。有一系列的相關(guān)對(duì)象,大多數(shù)都是可以在請(qǐng)求的每一個(gè)層次,通過(guò)實(shí)現(xiàn)其子類或者實(shí)現(xiàn)事件接口來(lái)進(jìn)行擴(kuò)展。通過(guò)這個(gè)機(jī)制能夠接觸到非常低層的接口,例如緩存,權(quán)限驗(yàn)證等。你甚至能在接受請(qǐng)求的前后過(guò)濾內(nèi)容,或者將滿足特定要求的請(qǐng)求轉(zhuǎn)到你的代碼或者其他的URL地址。有很多不同的方法來(lái)完成相同的事情,而且所有的這些方法實(shí)現(xiàn)的都非常直接,這樣,就可以靈活的根據(jù)性能以及開(kāi)發(fā)難度來(lái)選擇最好的方法.

整個(gè)的ASP.NET引擎都是托管代碼完成的,并且,可以支持通過(guò)托管代碼進(jìn)行拓展
整個(gè)的ASP.NET引擎都是托管代碼完成的,并且,可以支持通過(guò)托管代碼進(jìn)行拓展.這是一個(gè)對(duì).NET框架是否能夠開(kāi)發(fā)出久經(jīng)考驗(yàn)的、性能良好的框架的有力的證明。然而,給人印象最深刻的部分是ASP.NET的深思熟慮的架構(gòu),能夠使得這個(gè)結(jié)構(gòu)非常易用,提供了處理請(qǐng)求的任何一個(gè)部分的能力。
使用ASP.NET你能夠完成  以前是ISAPI擴(kuò)展和ISAPI篩選器領(lǐng)域的工作,雖然帶有一些局限性,但是比ASP要好很多。ISAPI是一個(gè)非常底層的Win32形式的API,它僅有非常貧乏的接口,非常難創(chuàng)建經(jīng)久耐用的應(yīng)用程序。由于ISAPI非常的底層而且非常快速,它處在非托管開(kāi)發(fā)層。這樣ISAPI有些時(shí)候主要用做連接其他的應(yīng)用程序平臺(tái)的橋。這并不意味著ISAPI已經(jīng)死了。事實(shí)上ASP.NET在微軟的平臺(tái)上,正是通過(guò)ISAPI的一個(gè)擴(kuò)展和ASP.NET的運(yùn)行時(shí)和IIS進(jìn)行交互的。ISAPI提供了Web服務(wù)器的核心接口,并且ASP.NET使用非托管的ISAPI代碼來(lái)向客戶端接收,發(fā)送數(shù)據(jù)。ISAPI提供的數(shù)據(jù),是通過(guò)一些通用的對(duì)象暴露出去的,像HttpRequest和HtttpReponse,他們通過(guò)托管代碼對(duì)象,以一個(gè)非常好的,易接觸的接口形式,對(duì)外暴露非托管代碼的內(nèi)容。
 
從瀏覽器到ASP.NET
讓我們從一個(gè)典型的ASP.NET Web Request的生命周期的最初開(kāi)始。一個(gè)請(qǐng)求,在瀏覽器里,在一個(gè)用戶輸入一個(gè)URL地址或者點(diǎn)擊一個(gè)超鏈接,或者提交一個(gè)HTML表單(一個(gè)post類型的請(qǐng)求)?;蛘咭粋€(gè)客戶端的程序會(huì)調(diào)用ASP.NET的WebService,這個(gè)WebService也使用過(guò)ASP.NET進(jìn)行服務(wù)的。在服務(wù)器端,IIS5或者6接收到請(qǐng)求。在最底層,ASP.NET通過(guò)一個(gè)ISAPI擴(kuò)展和IIS進(jìn)行交互。這樣的一個(gè)請(qǐng)求通常會(huì)被路由到一個(gè)以aspx為擴(kuò)展名的頁(yè)面文件,但是如何處理這個(gè)請(qǐng)求,完全取決與HTTP handler的實(shí)現(xiàn),這個(gè)handler為了處理指定的擴(kuò)展名而創(chuàng)立起來(lái)。在IIS里,.aspx 被‘應(yīng)用程序擴(kuò)展’(也可以成為腳本映射) 映射到ASP.NET ISAPI dll - ASP.NET_isapi.dll.每一個(gè)觸發(fā)ASP.NET的請(qǐng)求都是必須通過(guò)在ASP.NET_isapi.dll指明和注冊(cè)的擴(kuò)展名。
 
根據(jù)擴(kuò)展名,ASP.NET將請(qǐng)求路由到相應(yīng)的,負(fù)責(zé)響應(yīng)請(qǐng)求的handler。舉一個(gè)例子,asmx這是一個(gè)WebService的擴(kuò)展名,它不會(huì)被路由到磁盤(pán)上面的一個(gè)頁(yè)面文件,而是路由到一個(gè)WebService類里面。其他很多的映射已經(jīng)被ASP.NET安裝了,而且你也可以定義你自己的。所有的這些HttpHandlers都是在ASP.NET  ISAPI里面指出,從而在IIS里面被映射,或者在web.config文件里面設(shè)置,路由到指定的HTTP Handler的實(shí)現(xiàn)。每一個(gè)handler,是處理指定的擴(kuò)展名的一個(gè).NET類,這個(gè)類可以簡(jiǎn)單到一個(gè)HelloWorld程序,也可以非常復(fù)雜,像一個(gè)ASP.NET page類或者 WebService 的實(shí)現(xiàn)?,F(xiàn)在,理解擴(kuò)展名是這種映射機(jī)制的基礎(chǔ),這種機(jī)制是ASP.NET用來(lái)從IIS獲得一個(gè)用戶請(qǐng)求然后將其路由到指定的處理請(qǐng)求的handler。

ISAPI是第一個(gè)也是性能最高的定制web請(qǐng)求處理的切入點(diǎn)。
ISAPI 連接
ISAPI是一個(gè)非常低層的非托管的win32API。這個(gè)接口根據(jù)ISAPI定義規(guī)范,非常的簡(jiǎn)單,還有優(yōu)化過(guò)的性能。他們非常的底層-處理指針,用函數(shù)指針來(lái)進(jìn)行回調(diào)-它們?yōu)殚_(kāi)發(fā)者和工具提供最底層的,最好性能的,來(lái)處理IIS的接口。由于ISAPI非常的底層,他并適合創(chuàng)建應(yīng)用程序級(jí)別的代碼,而且,ISAPI趨向主要被用作 為高層工具提供應(yīng)用程序服務(wù)器功能的 橋接口。舉個(gè)例子,ASP和ASP.NET都是建立在ISAPI上面,還有Cold Fusion,運(yùn)行在IIS上面的,大多數(shù)的Perl,PHP以及JSP的實(shí)現(xiàn)還有很多的第三方的解決方案,比如我的Web Connection framework for Visual FoxPro都是建立在ISAPI上面的。ISAPI是一個(gè)為高層應(yīng)用程序提供接口的非常優(yōu)秀的工具,這些接口抽象了ISAPI提供的信息。在ASP.NET和ASP中,這些引擎將ISAPI提供的信息抽象為像Request和Response這樣的對(duì)象,使他們讀取到ISAPI的Request信息。把ISAPI想象成鉛錘。對(duì)于ASP.NET來(lái)說(shuō),ISAPI dll非常瘦小,僅僅是作為一個(gè)路由機(jī)制,以管道形式傳送請(qǐng)求到ASP.NET運(yùn)行時(shí),所有的重型的處理甚至請(qǐng)求的線程管理,都在ASP.NET引擎和你的代碼中。
 
依照協(xié)議,ISAPI支持ISAPI擴(kuò)展和ISAPI篩選器。擴(kuò)展是一個(gè)請(qǐng)求處理接口,并且提供處理web服務(wù)器的傳入傳出的邏輯,它本質(zhì)上是一個(gè)事務(wù)接口。ASP.NET和ASP都是做為ISAPI擴(kuò)展被實(shí)現(xiàn)的。ISAPI過(guò)濾器是一組接口,他們能 查看每一個(gè)進(jìn)入IIS的請(qǐng)求,修改內(nèi)容,或者改變類似于驗(yàn)證功能的行為。順便提一句,在ASP.NET中,通過(guò)兩個(gè)概念映射了類似ISAPI的功能:HTTPHandlers(擴(kuò)展)和HttpModules(篩選器)。一會(huì),我們看詳細(xì)的內(nèi)容。
 
ISAPI是標(biāo)記著ASP.NET的請(qǐng)求的初始代碼。ASP.NET映射了各種擴(kuò)展名到ISAPI的擴(kuò)展里,這些映射都在.NET Framework的目錄下:
<.NET FrameworkDir>\ASP.NET_isapi.dll
你可以交互式的在IIS服務(wù)管理器里面看到這些映射,如圖一.選擇你的網(wǎng)站,然后“主目錄”,“配置”,“映射”。


圖一: IIS 映射各種擴(kuò)展名到ASP.NET ISAPI,像 .ASPX 。通過(guò)這個(gè)機(jī)制,請(qǐng)求在web服務(wù)器層被路由到ASP.NET的處理管道。
 
你不應(yīng)該手動(dòng)的設(shè)置它們,因?yàn)?NET需要他們。另外你也可以使用ASP.NET_regiis.exe 工具來(lái)使得各種腳本映射得到正確的注冊(cè):
 
cd <.NetFrameworkDirectory>
ASP.NET_regiis - i
 
這就將會(huì)為整個(gè)的站點(diǎn)注冊(cè)特定版本的ASP.NET運(yùn)行時(shí),創(chuàng)建各種客戶端腳本庫(kù)。注意,這里是注冊(cè)在上面的文件夾安裝的特定版本的CLR 。ASP.NET_regiis命令的選項(xiàng)允許你可以單獨(dú)的設(shè)置一個(gè)虛擬目錄。每一個(gè)版本的.NET框架都有他自己版本的ASP.NET_regiis,你需要運(yùn)行一個(gè)恰當(dāng)版本的來(lái)注冊(cè)一個(gè)網(wǎng)站或者一個(gè)虛擬目錄。以ASP.NET2.0為例,你可以在IIS配置頁(yè)面里面的ASP.NET選項(xiàng)選擇.NET的版本。
 
IIS5和IIS6工作方式不同
當(dāng)一個(gè)請(qǐng)求進(jìn)入,IIS檢查腳本映射,然后將請(qǐng)求路由到ASP.NET_isapi.dll。這個(gè)DLL進(jìn)行的操作在IIS6和IIS5中明顯不同,圖2大概的展示了這個(gè)流程。
 
IIS5中直接宿主ASP.NET_isapi.dll在inetInfo.exe進(jìn)程中,或者一個(gè)獨(dú)立的進(jìn)程中。當(dāng)?shù)谝粋€(gè)請(qǐng)求來(lái)到這個(gè)DLL文件的時(shí)候,將會(huì)產(chǎn)生另外的一個(gè)新的進(jìn)程– ASP.NET_wp.exe –并且路由請(qǐng)求到這個(gè)新生成的進(jìn)程中。這個(gè)進(jìn)程一次的加載,宿主.NET運(yùn)行時(shí)。每一個(gè)請(qǐng)求都是先來(lái)到ISAPI然后通過(guò)命名管道路由到工作進(jìn)程

圖二– 從IIS到ASP.NET運(yùn)行時(shí)的. IIS 5 and IIS 6以不同的方式處理 ASP.NET,但是總的來(lái)說(shuō),一旦到了ASP.NET的管道,就相同了。

IIS6,不像以前的服務(wù)器,它是完全為ASP.NET做過(guò)優(yōu)化的
IIS 6 –應(yīng)用程序池萬(wàn)歲
IIS6明顯的改變了處理模型,IIS不再像ISAPI的擴(kuò)展一樣直接的處理任何不相關(guān)的可執(zhí)行代碼。取而代之的是,IIS6總是創(chuàng)建一個(gè)獨(dú)立的工作進(jìn)程—一個(gè)應(yīng)用程序池—并且所有的請(qǐng)求都在這個(gè)進(jìn)程里面,包括ISAPI dll的執(zhí)行。
應(yīng)用程序池是IIS6的一個(gè)重要改善,因?yàn)樗鼈冊(cè)试S非常細(xì)粒度的控制指定進(jìn)程執(zhí)行的東西??梢詾槊恳粋€(gè)虛擬目錄或者一個(gè)站點(diǎn)設(shè)置應(yīng)用程序池,所以你能夠?qū)⒚恳粋€(gè)web應(yīng)用程序分割到每一個(gè)進(jìn)程中,這個(gè)進(jìn)程和其他的應(yīng)用程序的進(jìn)程完全獨(dú)立。如果其中的一個(gè)進(jìn)程死掉了,也不會(huì)影響到其他的進(jìn)程。
 
另外,應(yīng)用程序池是高度可設(shè)置化的。你可以通過(guò)設(shè)置它的執(zhí)行模擬級(jí)別 來(lái)設(shè)置其執(zhí)行的安全環(huán)境,你可以為每一個(gè)Web應(yīng)用程序定制權(quán)限。為ASP.NET的一個(gè)重要的改進(jìn)就是應(yīng)用程序池取代了大多數(shù)的在machine.config的進(jìn)程模型。這在IIS里面比較難于管理,因?yàn)檫@個(gè)設(shè)置是全局的,而且不能在應(yīng)用程序web.config中被繼承。當(dāng)IIS6運(yùn)行的時(shí)候,進(jìn)程模型設(shè)置大部分被忽略了忽略,取而代之的是從應(yīng)用程序池中讀取。我這里是說(shuō)“大部分”,對(duì)于一些設(shè)置,像進(jìn)程池的大小,IO線程仍舊在里面(配置文件)設(shè)置,因?yàn)閼?yīng)用程序池里面沒(méi)有對(duì)應(yīng)的設(shè)置。
 
因?yàn)閼?yīng)用程序池是外部的可執(zhí)行的,而且這些可以很容易的進(jìn)行監(jiān)視和管理。IIS6提供了一些健康檢測(cè),重啟,超時(shí)的選項(xiàng),這些可以檢測(cè),大多數(shù)可以修正應(yīng)用程序的錯(cuò)誤。最后,IIS6的應(yīng)用程序池并不依賴COM+,和IIS5的獨(dú)立進(jìn)程一樣,它改進(jìn)了性能和穩(wěn)定性,尤其是內(nèi)部使用了COM對(duì)象的應(yīng)用程序。
 
盡管IIS6的應(yīng)用程序池區(qū)分于可執(zhí)行文件,他們通過(guò)直接的接觸HTTP.SYS核心模塊,而進(jìn)行了高度的HTTP操作優(yōu)化。進(jìn)入的請(qǐng)求,直接的路由到相應(yīng)的應(yīng)用程序池。InetInfo僅僅扮演了一個(gè)管理、設(shè)置服務(wù)-大多說(shuō)交互實(shí)際發(fā)生在HTTP.SYS和應(yīng)用程序之間,所有的這些構(gòu)成了一個(gè)比IIS5更加穩(wěn)定,更加高性能的環(huán)境。尤其是對(duì)于一些靜態(tài)內(nèi)容和ASP.NET應(yīng)用程序。
 
一個(gè)IIS6的應(yīng)用程序池也有ASP.NET內(nèi)在的認(rèn)識(shí),而且一個(gè)ASP.NET能夠和新的底層的API進(jìn)行交互,這使得ASP.NET直接接觸到HTTP Cache的API,也就使得能夠從ASP.NET層來(lái)控制Web服務(wù)器的緩存。
 
在IIS6,ISAPI擴(kuò)展運(yùn)行在一個(gè)應(yīng)用程序池的工作進(jìn)程中。.NET運(yùn)行時(shí)也運(yùn)行在這個(gè)進(jìn)程,所以.NET運(yùn)行時(shí)和ISAPI 擴(kuò)展的交互是進(jìn)程內(nèi)的,這樣肯定是比一定要使用命名管道接口的IIS5效率更高。盡管IIS兩個(gè)版本的宿主模型是不同的,但是到達(dá)托管代碼之后都是相同的了,只有在做請(qǐng)求路由的時(shí)候有一些不同。

ISAPIRuntime.ProcessRequest() 方法是進(jìn)入ASP.NET的第一個(gè)入口
進(jìn)入到.NET運(yùn)行時(shí)
實(shí)際上進(jìn)入.NET運(yùn)行時(shí),是通過(guò)一系列沒(méi)有給出文檔說(shuō)明的類和接口。通過(guò)微軟,很少能夠了解這些接口和類,而且微軟并不想談?wù)撨@些細(xì)節(jié),因?yàn)樗麄冋J(rèn)為這些實(shí)現(xiàn)的細(xì)節(jié)幾乎對(duì)創(chuàng)建一個(gè)ASP.NET應(yīng)用程序沒(méi)有影響。
 
工作進(jìn)程ASP.NET_WP.EXE (IIS5)和W3WP.EXE (IIS6)宿主.NET運(yùn)行時(shí),并且ISAPI DLL 通過(guò)底層的COM調(diào)用了一些少量的非托管接口,而最終調(diào)用到了一個(gè)ISAPIRuntime的子類。第一個(gè)入口就是這個(gè)沒(méi)有文檔說(shuō)明的ISAPIRuntime類,它通過(guò)COM將IISAPIRuntime接口暴露給調(diào)用者。這些COM接口,底層的以不了解的接口,意味著從ISAPI擴(kuò)展到ASP.NET的內(nèi)部調(diào)用。圖3顯示了這個(gè)接口,在Lutz Roeder的優(yōu)秀的.NET Reflector 工具(http://www./roeder/dotnet/)。反射程序集視圖和反編譯器,這使得我們能夠非常容易的看到反編譯的代碼(用IL,C#,VB),這是一個(gè)非常好的方法來(lái)談就這樣的過(guò)程。

圖3 –如果你想深入研究底層的接口,打開(kāi)Reflector,并且打開(kāi) System.Web.Hosting 命名空間。進(jìn)入ASP.NET的入口點(diǎn)通過(guò)COM接口而被ISAPI dll調(diào)用,這就獲得了一個(gè)非托管的指針,指向了ISAPI ECB.。ECB包含了訪問(wèn)ISAPI的全部接口,能夠獲得請(qǐng)求的數(shù)據(jù),也能夠?qū)?shù)據(jù)返回給IIS。
 
IISAPIRuntime接口在ISAPI擴(kuò)展的非托管代碼和托管代碼之間。如果你看一個(gè)這個(gè)類,你會(huì)發(fā)現(xiàn)有一個(gè)簽名如下的方法:
 
[return: MarshalAs(UnmanagedType.I4)]
int ProcessRequest([In] IntPtr ecb,
                   [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
 
ecb這個(gè)參數(shù)是ISAPI擴(kuò)展控制模塊(Extension Control Block –ECB),這個(gè)方法將非托管的資源傳到ProcessRequest方法中。這個(gè)方法獲得ECB并且 通過(guò)Request和Response對(duì)象將它作為輸入輸出的基礎(chǔ)。一個(gè)ISAPI ECB包含了所有的底層請(qǐng)求信息,包括服務(wù)器信息,一個(gè)來(lái)組織變量的輸入流同時(shí)也有一個(gè)用來(lái)寫(xiě)回?cái)?shù)據(jù)給客戶端的輸出流。這個(gè)單獨(dú)的ecb基本提供了所有的ISAPI請(qǐng)求的所有的功能,而ProcessRequest方法是這個(gè)資源和托管代碼交互的出口和入口。
 
ISAPI擴(kuò)展異步的處理請(qǐng)求。這個(gè)模型中,ISAPI擴(kuò)展立刻回報(bào)給工作進(jìn)程或者IIS線程,而保持當(dāng)前的請(qǐng)求的ECB存活。ECB包括一個(gè)機(jī)制允許ISAPI知道請(qǐng)求已經(jīng)完成(通過(guò)ecb.ServerSupportFunction),然后釋放ECB。這個(gè)異步處理立刻釋放ISAPI工作進(jìn)程,并且卸載進(jìn)程,而轉(zhuǎn)到由一個(gè)ASP.NET管理的獨(dú)立的線程。
 
ASP.NET接收到這個(gè)ecb引用,用它來(lái)獲取一些關(guān)于當(dāng)前請(qǐng)求的信息,比如服務(wù)器變量,POST數(shù)據(jù),也包括向服務(wù)器返回輸出數(shù)據(jù)。ECB存活到請(qǐng)求完成,或者IIS超時(shí)并且,ASP.NET會(huì)繼續(xù)和其進(jìn)行交互,直至請(qǐng)求完畢。輸出被寫(xiě)入到ISAPI的輸出流(ecb.WriteClient()),并且,當(dāng)請(qǐng)求完成的時(shí)候,ISAPI擴(kuò)展被通知請(qǐng)求完成,ECB可以被釋放。這個(gè)實(shí)現(xiàn)非常的高效,因?yàn)?NET類完全扮演的是對(duì)高性能的非托管代碼簡(jiǎn)單的封裝,

加載.NET - 有一些神秘Loading .NET
讓我們回到這里的一個(gè)步驟:我跳過(guò)了.NET運(yùn)行時(shí)是怎么加載的。這里的事情有一點(diǎn)模糊,這個(gè)過(guò)程,我沒(méi)有獲得任何的文檔,并且我們?cè)谡劚镜卮a,而又沒(méi)有簡(jiǎn)單的方法反編譯ISAPI DLL把它找出來(lái)。
 
我的最好的猜想是,當(dāng)?shù)谝粋€(gè)ASP.NET映射擴(kuò)展名被請(qǐng)求的時(shí)候,ISAPI擴(kuò)展引導(dǎo)了.NET運(yùn)行時(shí)。一旦這個(gè)運(yùn)行時(shí)存在了之后,如果當(dāng)前沒(méi)有,非托管代碼可以為給定的虛擬路徑請(qǐng)求一個(gè)ISAPIRuntime的實(shí)例對(duì)象。每一個(gè)虛擬目錄都會(huì)有它自己的AppDomain(應(yīng)用程序域),在AppDomain里,ISAPIRuntime存在于一個(gè)獨(dú)立的應(yīng)用程序引導(dǎo)的過(guò)程。實(shí)例化看起來(lái)像發(fā)生在COM里面,因?yàn)榻涌诜椒ㄊ且粋€(gè)COM可調(diào)用的方法。
 
為了創(chuàng)建ISAPIRuntime實(shí)例,System.Web.Hosting.AppDomainFactory.Create()方法被調(diào)用,當(dāng)某一個(gè)虛擬目錄第一次被請(qǐng)求的時(shí)候。這開(kāi)始了‘應(yīng)用程序’引導(dǎo)過(guò)程。這個(gè)調(diào)用獲得了參數(shù)的類型,模塊的名稱以及虛擬路徑的信息—對(duì)于應(yīng)用程序來(lái)說(shuō),這個(gè)ASP.NET用來(lái)創(chuàng)建一個(gè)AppDomain以及運(yùn)行指定虛擬目錄的ASP.NET應(yīng)用程序。這個(gè)HttpRuntime派生對(duì)象在一個(gè)新的AppDomain里面被創(chuàng)建。每一個(gè)虛擬目錄都被宿主在一個(gè)獨(dú)立的AppDomain中,僅僅加載指定的應(yīng)用程序的請(qǐng)求。ISAPI擴(kuò)展來(lái)管理HttpRuntime對(duì)象的實(shí)例,路由相應(yīng)的請(qǐng)求到正確的請(qǐng)求的虛擬路徑上。

圖4 – 從ISAPI請(qǐng)求到ASP.NET的HTTP管道的傳輸過(guò)程,使用到了一些沒(méi)有文檔說(shuō)明的類和接口,并且需要一些工廠方法調(diào)用。通過(guò)其調(diào)用者把一個(gè)引用放在IISAPIRuntime接口中(這個(gè)接口觸發(fā)了ASP.NET請(qǐng)求處理)每一個(gè)Web應(yīng)用程序運(yùn)行在其自身的AppDomain中.
回到運(yùn)行時(shí)
在這時(shí),我們已經(jīng)有了一個(gè)被ISAPI擴(kuò)展激活,可被其調(diào)用的ISAPIRuntime的實(shí)例。一旦運(yùn)行時(shí)運(yùn)行起來(lái),ISAPI代碼將會(huì)調(diào)用到ISAPIRuntime.ProcessRequest()這個(gè)方法,這個(gè)方法是真正的進(jìn)入ASP.NET管道的入口。這個(gè)流程已經(jīng)顯示在圖4中。
 
記住,ISAPI是一個(gè)多線程的,這樣請(qǐng)求將會(huì)以多線程的方式進(jìn)如ASP.NET,通過(guò)ApplicationDomainFactory.Create()返回的引用。列表1顯示了IsapiRuntime.ProcessRequest反編譯的結(jié)果,這個(gè)方法接收一個(gè)ISAPI ecb對(duì)象,這個(gè)方法是線程安全的,所以,多線程的ISAPI可以同時(shí)的安全的調(diào)用這個(gè)單獨(dú)返回的對(duì)象實(shí)例。
Listing 1: 處理請(qǐng)求的方法獲得了一個(gè)ISAPI Ecb并且將其傳入工作進(jìn)程

Code

這里的代碼并不重要,并且記住,這是你將從來(lái)不會(huì)直接接觸到的,反編譯出來(lái)的框架內(nèi)部的代碼,而且這些代碼可能在以后會(huì)改變。這意味著證實(shí)在后臺(tái)發(fā)生了什么。ProcessRequest接收到非托管的ECB引用,并且將它傳送到ISAPIWorkerRequest對(duì)象中,這個(gè)對(duì)象負(fù)責(zé)為當(dāng)前請(qǐng)求創(chuàng)建請(qǐng)球上下文,就像在Listing2中顯示的一樣。
 
System.Web.Hosting.ISAPIWorkerRequest類是HttpWorkerRequest的一個(gè)抽象子類。它的職責(zé)是為輸入輸出創(chuàng)建一個(gè)抽象的視圖,這個(gè)作為整個(gè)web應(yīng)用程序的輸入。注意另外一個(gè)工廠方法: CreateWorkerRequest,做為第二個(gè)參數(shù),它接收到了要?jiǎng)?chuàng)建的worker request的類型,這里有三種不用的版本:ISAPIWorkerRequestInProc, ISAPIWorkerRequestInProcForIIS6, ISAPIWorkerRequestOutOfProc。這個(gè)對(duì)象在每一個(gè)請(qǐng)求進(jìn)來(lái)之后被創(chuàng)建,這個(gè)對(duì)象是Request和Response對(duì)象基礎(chǔ),他們從WorkerRequest里接收他們的數(shù)據(jù)和流。
 
這個(gè)HttpWorkerRequest抽象類用來(lái)為底層的接口提供一個(gè)高層的抽象,所以不管這個(gè)數(shù)據(jù)是來(lái)自一個(gè)CGI Web服務(wù)器,一個(gè)瀏覽器,或者一些訂制的機(jī)制。關(guān)鍵的是ASP.NET能夠以同樣的方式來(lái)獲得信息。
 
對(duì)于IIS的抽象,以一個(gè)ISAPI ECB模塊。在我們的請(qǐng)求過(guò)程中,ISAPIWorkRequst和IISAPI ECB交互,當(dāng)需要的時(shí)候,通過(guò)它獲得數(shù)據(jù)。Listing2顯示展示了如何獲得query string 的值。
Listing 2: 一個(gè)使用非托管的ISAPIWorkerRequest 的方法

Code

ISAPIWorkerRequest實(shí)現(xiàn)了一個(gè)高層的封裝的方法,這個(gè)方法調(diào)用底層的用來(lái)和訪問(wèn)底層非托管的API的Core方法。Core 方法在ISAPIWorkerRequest實(shí)例的子類中實(shí)現(xiàn),這樣,為其宿主的環(huán)境提供實(shí)現(xiàn)方法。這樣構(gòu)造了一個(gè)更加容易插拔的環(huán)境,方便添加一些新的服務(wù)器或者服務(wù)ASP.NET的其他平臺(tái)的接口的實(shí)現(xiàn)。也有一個(gè)輔助類System.Web.UnsafeNativeMethods。很多的這些方法,他們就是通過(guò)操作ISAPI ECB結(jié)構(gòu)體來(lái)完成對(duì)ISAPI擴(kuò)展的調(diào)用。

HttpRuntime, HttpContext, and HttpApplication
當(dāng)一個(gè)請(qǐng)求到達(dá),被路由到ISAPIRuntime.ProcessRequest()方法。這個(gè)方法繼而調(diào)用HttpRuntime.ProcessRequest,這個(gè)方法做了很多重要的事情(使用Reflector 查看System.Web.HttpRuntime.ProcessRequestInternal):
 
•為請(qǐng)求創(chuàng)建一個(gè)新的HttpContext實(shí)例
•得到一個(gè)HttpApplication 實(shí)例
•調(diào)用 HttpApplication.Init() 來(lái)建立管道事件。
•Init() 觸發(fā) HttpApplication.ResumeProcessing(),這個(gè)方法開(kāi)始ASP.NET管道處理
 
首先,一個(gè)新的HttpContext對(duì)象被封裝了ISAPI ECB的 ISAPIWorkerRequest創(chuàng)建,傳遞。這個(gè)Context在整個(gè)的請(qǐng)求生命周期中都可用,總是可以通過(guò)靜態(tài)的HttpContext.Current 屬性來(lái)獲得。就像名字提示的,HttpContext對(duì)象表示了當(dāng)前激活的請(qǐng)求的上下文,因?yàn)樗嗽谡?qǐng)求的生命周期中,所有的你要接觸到的重要的典型的對(duì)象: Request, Response, Application, Server, Cache.在處理請(qǐng)求的任何時(shí)候,HttpContext.Current讓你接觸到所有的這些對(duì)象。
 
HttpContext對(duì)象同時(shí)包含了一個(gè)非常有用的集合,允許你用來(lái)存儲(chǔ)一些請(qǐng)求指定的數(shù)據(jù)。Context對(duì)象從請(qǐng)求周期被創(chuàng)建,當(dāng)請(qǐng)求完成的時(shí)候釋放,所以存儲(chǔ)在這個(gè)集合的數(shù)據(jù)僅僅對(duì)應(yīng)當(dāng)前的請(qǐng)求。一個(gè)很好的應(yīng)用的例子是一個(gè)時(shí)間請(qǐng)求記錄的機(jī)制,這里你想記錄請(qǐng)求開(kāi)始和請(qǐng)求結(jié)束的時(shí)間,通過(guò)在Listing3所示,在Global.asax 里面,Application_BeginRequest 和Application_EndRequest 方法。HttpContext是你的朋友,你可以自由的使用,在不同的請(qǐng)求或者頁(yè)面處理的部分。

Listing 3 – 使用 HttpContext.Items 結(jié)合來(lái)讓你在管道事件之間保存數(shù)據(jù)

Code

一旦Context對(duì)象被創(chuàng)立,ASP.NET需要路由進(jìn)入的請(qǐng)求到相應(yīng)的應(yīng)用程序/虛擬目錄,通過(guò)一個(gè)HttpApplication對(duì)象。每一個(gè)ASP.NET應(yīng)用程序被需建立一個(gè)虛擬目錄(或者根根目錄)每一個(gè)‘application’獨(dú)立的處理。

HttpApplication像一個(gè)典禮的主人,處理動(dòng)作在這里開(kāi)始

你的域的主人: HttpApplication
每一個(gè)請(qǐng)求都被路由到一個(gè)HttpApplication對(duì)象。HttpApplicationFactory類根據(jù)你的ASP.NET應(yīng)用程序的負(fù)載情況,為其創(chuàng)建一個(gè)HttpApplication對(duì)象池,并且為每一個(gè)請(qǐng)求處理這些引用。這個(gè)池的容量受限于設(shè)置在machine.config的ProcessModel鍵里面的MaxWorkerThreads的值,默認(rèn)值是20.
 
然而這個(gè)池只啟動(dòng)了少量對(duì)象,通常是一個(gè)然后同時(shí)進(jìn)入的請(qǐng)求多了,池中對(duì)象將會(huì)增長(zhǎng)。池是被監(jiān)視的,在負(fù)載量大的時(shí)候,將會(huì)增長(zhǎng)到它的容量的最大值,當(dāng)負(fù)載下降的時(shí)候,池的容量又會(huì)降低。
 
HttpApplication是你指定的Web應(yīng)用程序的外部容器,并且它映射到定義在Global.asax的文件中。他是進(jìn)入HttpRuntime的第一個(gè)點(diǎn),如果你看Global.asax(或者其后置代碼)你會(huì)發(fā)現(xiàn),他是派生自HttpApplication的一個(gè)類:public class Global : System.Web.HttpApplication
HttpApplication的主要目的是扮演了Http管道的事件控制者,所以它的接口主要由事件組成。事件是多方面的,包括:
•BeginRequest
•AuthenticateRequest
•AuthorizeRequest
•ResolveRequestCache
•AquireRequestState
•PreRequestHandlerExecute
•Handler Execution
•PostRequestHandlerExecute
•ReleaseRequestState
•UpdateRequestCache
•EndRequest
 
這些事件都在Global.asax文件中通過(guò)以Application_為前綴的空方法實(shí)現(xiàn)。舉個(gè)例子,Application_BeginRequest(), Application_AuthorizeRequest().這樣的處理方法,非常的方便,因?yàn)樵趹?yīng)用程序中,他們經(jīng)常會(huì)被用到,這樣你就不用顯示的創(chuàng)建事件處理的委托。
 
理解每一個(gè)ASP.NET虛擬用應(yīng)程序運(yùn)行在它自己的AppDomain中,然而在這個(gè)AppDomain中,有多個(gè)HttpApplication實(shí)例在同時(shí)運(yùn)行,被一個(gè)ASP.NET的一個(gè)池來(lái)進(jìn)行管理。這樣,多個(gè)請(qǐng)求就可以同時(shí)被處理,而且沒(méi)有互相的影響。
來(lái)看一看AppDomain,線程和HttpApplication的關(guān)系,看看Listing4的代碼。
Listing 4 – 顯示了AppDomain,線程和HttpApplication實(shí)例的關(guān)系

Code

這部分代碼運(yùn)行的結(jié)果在圖5中顯示,在兩個(gè)不同的瀏覽器中來(lái)訪問(wèn)這個(gè)示例頁(yè)面,來(lái)看這不用的Id

圖 5 –通過(guò)同時(shí)用兩個(gè)瀏覽器訪問(wèn),你可以看到AppDomain,Application池還有請(qǐng)求進(jìn)程怎樣互相影響,當(dāng)多個(gè)請(qǐng)求到達(dá)的時(shí)候,你會(huì)發(fā)現(xiàn)線程,Application的Id都改變了,AppDomain卻保持不變。
 
你會(huì)注意到,AppDomain 的ID保持不變,而HttpApplication的ID在大多數(shù)請(qǐng)求中都變化了,盡管他們有可能會(huì)重復(fù)。這些HttpApplication用過(guò)了,會(huì)在后面的請(qǐng)求中復(fù)用,所以Id是會(huì)重復(fù)出現(xiàn)的。注意,Application實(shí)例并不綁定到指定的線程,他們只是被分配到當(dāng)前請(qǐng)求的線程中。
 
線程來(lái)自 .NET的ThreadPool,而且,默認(rèn)的是Multithreaded Apartment (MTA)這種形式的線程。你可以在ASP.NET頁(yè)面中重寫(xiě)這部分,通過(guò)在@Page 指令設(shè)置ASPCOMPAT="true" 屬性,ASPCOMPAT意味著提供給COM組建一個(gè)安全的環(huán)境來(lái)運(yùn)行,并且,ASPCOMPAT使用特殊的Single Threaded Apartment (STA)線程來(lái)服務(wù)這些請(qǐng)求。STA的線程被擱置、合并,因?yàn)樗麄冃枰厥獾奶幚怼?br> 
事實(shí)上,HttpApplication對(duì)象都在同一個(gè)AppDomain中非常重要。這正是ASP.NET怎樣保證修改了web.config或者獨(dú)立的頁(yè)面,可以被整個(gè)的AppDomain所識(shí)別。在web.config里面修改值,可以引起AppDomain的關(guān)閉和重啟,這使得所有的HttpApplication都發(fā)現(xiàn)了這個(gè)變化,因?yàn)锳ppDomain重新加載的時(shí)候,他重新讀取了信息。所有的靜態(tài)成員都重新加載了,因?yàn)锳ppDomain重新加載,所以,應(yīng)用程序從應(yīng)用程序配置文件讀取設(shè)置的時(shí)候這些值都被更新了。
 
來(lái)看這個(gè)例子。訪問(wèn)ApplicationPoolsAndThreads.aspx頁(yè)面,并且注意AppDomain的Id。然后修改一下web.config(添加一個(gè)空格并且保存)。然后重新讀取這個(gè)頁(yè)面,你會(huì)發(fā)現(xiàn)AppDomain已經(jīng)被重新創(chuàng)建了。
 
本質(zhì)上,當(dāng)這發(fā)生后,web應(yīng)用程序/虛擬目錄完全的‘重啟’了。所有的已經(jīng)在管道中的請(qǐng)求,將會(huì)繼續(xù)的通過(guò)現(xiàn)在已經(jīng)存在的管道繼續(xù)運(yùn)行,同時(shí),所有的新的請(qǐng)求都會(huì)被路由到新的AppDomain。為了處理那些‘掛起的請(qǐng)求’,在這些請(qǐng)求超時(shí)后或者甚至請(qǐng)求還在進(jìn)行的時(shí)候,ASP.NET會(huì)強(qiáng)制關(guān)閉舊的AppDomain。這樣,實(shí)際上在某一特定時(shí)間點(diǎn),可以存在兩個(gè)相同的AppDomain,為同一個(gè)HttpApplication服務(wù),舊的AppDomain關(guān)閉,新的AppDomain里面的Application對(duì)象將會(huì)急劇增加。兩個(gè)AppDomain都繼續(xù)服務(wù),直到舊的一個(gè)將所有的請(qǐng)求都運(yùn)行完畢,舊的將會(huì)被關(guān)閉,只留下新的AppDomain。

ASP.NET管道的流程

HttpApplication通過(guò)觸發(fā)指示你的應(yīng)用程序狀態(tài)的事件,來(lái)負(fù)責(zé)請(qǐng)求的流程。這發(fā)生在HttpApplication.Init()方法中(用Reflector看System.Web.HttpApplication.InitInternal 和 HttpApplication.ResumeSteps() ),這個(gè)方法連續(xù)的創(chuàng)建并觸發(fā)了一系列的事件,包括了調(diào)用執(zhí)行所有的handlers.事件處理自動(dòng)的映射在global.asax里面的事件,并且,他們也映射所有已經(jīng)附加了的HTTPModule,本質(zhì)上,HTTPModule是一個(gè)形象化了事件槽。
 
HttpModules 和 HttpHandlers a都通過(guò)web.config里面條目而被動(dòng)態(tài)加載,并且將其綁定到事件鏈。HttpModules是實(shí)際的HttpApplication的事件處理者,而HttpHandlers是一個(gè)終點(diǎn),用來(lái)處理‘應(yīng)用程序級(jí)的請(qǐng)求處理’的。HttpModules 實(shí)際是HttpApplication的事件處理器。
Modules 和 Handlers的加載,附加到調(diào)用鏈都做為HttpApplication.Init()方法的一部分、圖6顯示了各種事件以及他們出發(fā)的時(shí)間和觸發(fā)影響的部分。

圖 6 – ASP.NET HTTP管道的事件流程。HttpApplication對(duì)象的事件驅(qū)動(dòng)貫穿管道。 Http Modules能夠攔截這些事件,進(jìn)而重寫(xiě)或者增強(qiáng)已有的功能。
HttpContext, HttpModules 和 HttpHandlers
HttpApplication自身并不知道傳送進(jìn)來(lái)的數(shù)據(jù),他僅僅是一個(gè)通信對(duì)象,通過(guò)事件來(lái)進(jìn)行交互。他觸發(fā)事件,并且將信息通過(guò)HttpContext對(duì)象傳遞到被調(diào)用的方法中。當(dāng)前請(qǐng)求的狀態(tài)數(shù)據(jù)存儲(chǔ)在我們前面提到的Httpcontext對(duì)象。它提供了所有請(qǐng)求的數(shù)據(jù),并且在管道中,伴隨著每一個(gè)請(qǐng)求從開(kāi)始到結(jié)束。圖7顯示了通過(guò)ASP.NET管道的流程,注意Context對(duì)象從開(kāi)始到請(qǐng)求的結(jié)束,都可以用來(lái)存儲(chǔ)信息,在一個(gè)事件方法中存貯信息,在后面的事件方法中獲得這個(gè)數(shù)據(jù)。
 
一旦管道開(kāi)始,HttpApplication如圖6一樣,開(kāi)始一個(gè)接著一個(gè)的觸發(fā)事件。每一個(gè)事件處理器被調(diào)用,如果事件被調(diào)用這些處理器執(zhí)行他們的任務(wù)。這個(gè)過(guò)程的主要目的是最終調(diào)用HttpHandler處理一個(gè)請(qǐng)求。Handlers是處理ASP.NET請(qǐng)求的核心的機(jī)制,經(jīng)常位于應(yīng)用程序級(jí)代碼的執(zhí)行。記住,ASP.NET 頁(yè)面和WebService框架,都是作為HTTPHandler的實(shí)現(xiàn),在這里請(qǐng)求的核心處理過(guò)程被執(zhí)行。Modules往往是更核心的性質(zhì),用來(lái)準(zhǔn)備或者發(fā)送處理交付于Handlers的Context。ASP.NET中,典型的默認(rèn)的Handlers是Authentication, pre-processing的Caching 和 發(fā)送處理請(qǐng)求的編碼機(jī)制。
 
有很多的信息在HttpHandlers 和 HttpModules中,但是為了保持這篇文章的一個(gè)合理的長(zhǎng)度,我下面只是簡(jiǎn)短介紹一下handlers。
HttpModules
隨著請(qǐng)求通過(guò)管道,一系列的事件在HttpApplication對(duì)象中被觸發(fā)。我們已經(jīng)在Global.asax中看到了這些事件。這種方法是應(yīng)用程序指定的,這樣,就不一定總是你想要的。如果你想創(chuàng)建一個(gè)廣義的HttpApplication事件處理,而且能夠可插拔的放到任何一個(gè)應(yīng)用程序中,你可以使用HttpModules,他們是可以復(fù)用的,而且,不需要程序代碼指定,只需要在web.config里面設(shè)置一下。
 
Modules本質(zhì)上像篩選器,就像一個(gè)在ASP.NET請(qǐng)求層的ISAPI Filter。Modules允許附加事件到每一個(gè)通過(guò)ASP.NET HttpApplication對(duì)象的請(qǐng)求。這些Module在外部的程序集的類里面,在web.config里面設(shè)置,并且當(dāng)應(yīng)用程序啟動(dòng)的時(shí)候,隨著應(yīng)用程序的啟動(dòng)而加載。通過(guò)實(shí)現(xiàn)指定的接口和方法,將事件添加到HttpApplication事件鏈中。多個(gè)HttpModules能夠附加事件處理代碼到相同的事件上,這些附加的事件處理的順序根據(jù)在web.config里面設(shè)置的一樣:

<configuration>
  
<system.web>
    
<httpModules>
  
<add name= "BasicAuthModule" 
      type
="HttpHandlers.BasicAuth,WebStore" />
    
</httpModules>
  
</system.web>
</configuration>

注意,你需要指定一個(gè)類型全名,還有一個(gè)程序集的名字.
 
Modules允許你看到每一個(gè)請(qǐng)求并且基于觸發(fā)事件形式的執(zhí)行動(dòng)作。Module非常適于修改request或者response的內(nèi)容,用以提供定制的身份驗(yàn)證或者為每一個(gè)請(qǐng)求執(zhí)行預(yù)處理。很多的ASP.NET的特性,像身份驗(yàn)證以及Sesion引擎都是通過(guò)HTTPModule來(lái)實(shí)現(xiàn)的。

雖然,HTTPModule感覺(jué)上像ISAPI Filter,他們可以查看每一個(gè)通過(guò)ASP.NET應(yīng)用程序的請(qǐng)求,但是他們只是能夠監(jiān)視映射到ASP.NET應(yīng)程序或者ASP.NET虛擬目錄的請(qǐng)求。這樣,你可以查看ASPX文件,或者其他的映射到ASP.NET的擴(kuò)展名。但是你不能監(jiān)視一個(gè)標(biāo)準(zhǔn)的.HTM或者圖片文件,除非你把他們的擴(kuò)展名顯式的映射到ASP.NET ISAPI dll,就像圖1顯式的。一個(gè)Module的經(jīng)常被用作,過(guò)濾指定文件夾下面的圖片,然后顯式一個(gè)‘SAMPLE’覆蓋在每一個(gè)圖片上面,通過(guò)使用GDI+。(譯者:水???)
 
實(shí)現(xiàn)一個(gè)HTTPModule非常的容易:你必須實(shí)現(xiàn)IHttpModule接口,這個(gè)接口僅僅有兩個(gè)方法,Init()和Dispose().這個(gè)時(shí)間參數(shù)是一個(gè)HttpApplication對(duì)象,通過(guò)他你可以訪問(wèn)Httpcontext對(duì)象,在這個(gè)方法中你可以接觸到HttpApplication的事件。舉個(gè)例子,如果你想附加AuthenticateRequest事件你可以像Listing5一樣。
Listing 5: 一個(gè)基礎(chǔ)的HTTP Module非常好實(shí)現(xiàn)

Code

記住,你的Module訪問(wèn)的是HttpContext對(duì)象,通過(guò)其就能獲得其他的管道對(duì)象,就如Response和Request,這樣你可以獲得輸入等等,但是記住,這些不一定在后面的事件鏈中還有效。
 
你可以附件多個(gè)事件在Init()方法中,這樣你就可以通過(guò)一個(gè)Module來(lái)管理不同的功能操作。然而,最好將不同的邏輯放在不同的類中使得Module真正的模塊化。在你要實(shí)現(xiàn)的很多功能中,你需要附加到多個(gè)事件-舉個(gè)例子,一個(gè)日志filter需要早BeginRequest中記錄Request的開(kāi)始時(shí)間,而在EndRequest中記錄請(qǐng)求的完成時(shí)間。
 
注意HttpModules 和 HttpApplication 的事件events: Response.End() 或者 HttpApplication.CompleteRequest()將會(huì)切斷HttpApplication對(duì)象或者M(jìn)odule的事件鏈。See the sidebar “Watch out for Response.End() “ for more info.

HttpHandlers
Modules相當(dāng)?shù)牡讓佣?,?duì)應(yīng)的是對(duì)應(yīng)ASP.NET應(yīng)用程序的每一個(gè)請(qǐng)求。HTTP Handler則更加側(cè)重于對(duì)于一個(gè)指定的請(qǐng)求的操作,通常一個(gè)頁(yè)面都被映射到Handler.
 
實(shí)現(xiàn)Http Handler要求非?;A(chǔ),但是通過(guò)訪問(wèn)HttpContext對(duì)象可以獲得強(qiáng)大的功能。Http Handler通過(guò)實(shí)現(xiàn)一個(gè)非常簡(jiǎn)單的接口IHttpHandler來(lái)實(shí)現(xiàn)(或者其異步的版本的IHttpAsyncHandler),它僅僅包含了一的方法ProcessRequest()和一個(gè)IsReusagable屬性。關(guān)鍵的是,ProcessRequest()獲得了一個(gè)HttpContext對(duì)象的實(shí)例,這個(gè)方法負(fù)責(zé)處理Web請(qǐng)求,從開(kāi)始到結(jié)束。
 
一個(gè)簡(jiǎn)單的方法?非常的簡(jiǎn)單,對(duì)么?確實(shí),一個(gè)簡(jiǎn)單的接口,但是卻不是那么的簡(jiǎn)單!記得WebForm和WebService都是做為HTTPHandler的實(shí)現(xiàn)的,所以,很強(qiáng)大的功能封裝在這一個(gè)看似簡(jiǎn)單的接口中。關(guān)鍵點(diǎn)是,接觸到HTTP Handler的時(shí)候,ASP.NET的內(nèi)置對(duì)象已經(jīng)為開(kāi)始處理請(qǐng)求而創(chuàng)建和設(shè)置好了。關(guān)鍵就是HttpContext對(duì)象,他提供了所有的請(qǐng)求相關(guān)的功能獲得輸入信息,輸出信息到Web服務(wù)器。
 
一個(gè)HTTPHandler所有的動(dòng)作發(fā)生都是通過(guò)調(diào)用這個(gè)單獨(dú)的ProcessRequest().這個(gè)可以簡(jiǎn)單的像:

public void ProcessRequest(HttpContext context)
{
context.Response.Write(
"Hello World");
}

也可以完全實(shí)現(xiàn)一個(gè)象WebForm Page引擎,可以輸出復(fù)雜格式HTML模板。這點(diǎn)完全取決與你的決定,你到底如何用這個(gè)簡(jiǎn)單,卻有強(qiáng)大的接口!
 
因?yàn)槟憧梢允褂肅ontext對(duì)象,你可以獲得Request,Response,Session和Cache對(duì)象,這樣你有了所有的ASP.NET請(qǐng)求的特性, 你可以找到用戶提交的內(nèi)容,也可以設(shè)置返回客戶端的內(nèi)容。記住Context對(duì)象,他是你的朋友,在這個(gè)ASP.NET請(qǐng)求的生命周期中!
 
Handler的關(guān)鍵性的操作應(yīng)帶是最終的把output輸出結(jié)果到Response對(duì)象,或者更具體的說(shuō)是Response對(duì)象的OutputStream。這個(gè)output返回客戶端的信息。在背后,ISAPIWorkerRequest負(fù)責(zé)將OutputStream返回到ISAPI ecb.WriteClient方法,執(zhí)行了IIS輸出的過(guò)程。

圖 7 – ASP.NET請(qǐng)求管道流程通過(guò)一系列事件接口,提供了很大的靈活性。Application扮演了一個(gè)宿主容器的角色,它加載了Web應(yīng)用程序,并且隨著請(qǐng)求的進(jìn)入和在管道中的傳遞而觸發(fā)事件。每一個(gè)請(qǐng)求都是通過(guò)相同的路徑,通過(guò)HTTP Filters和設(shè)置了的Modules。Filters能夠檢測(cè)通過(guò)管道的每一個(gè)請(qǐng)求,而Handlers允許實(shí)現(xiàn)用用程序的邏輯和接口,就像WebForm和WebService。為了提供應(yīng)用程序的輸入和輸出Context在整個(gè)過(guò)程提供了請(qǐng)求的相關(guān)信息。
 
WebForm在這個(gè)基礎(chǔ)框架上面,通過(guò)實(shí)現(xiàn)一個(gè)HTTPHandler以及更高層的接口,然而最終,Wenforms的Render()方法簡(jiǎn)單的使用一個(gè)HtmlTextWriter對(duì)象將其最終的輸出結(jié)果寫(xiě)入到context.Response.OutputStream。這樣,最終,一個(gè)高層的工具,像WebForms僅僅是一個(gè)Request和Response對(duì)象的高層的抽象。
 
你可能想知道,這點(diǎn)上,你是否需要處理HTTPHandler。畢竟,WebForm提供了簡(jiǎn)單的可訪問(wèn)的HTTPHandler實(shí)現(xiàn),那么我們?yōu)槭裁匆艞夁@個(gè)靈活性而不厭其煩的做一些底層的事情呢?
 
WebForm對(duì)于生成復(fù)雜的HTML頁(yè)面和需要圖形布局工具,模板化頁(yè)面的商業(yè)邏輯非常的好。但是,WebForm執(zhí)行了很多的增加消耗的任務(wù)。如果你僅僅想在系統(tǒng)中讀取一個(gè)文件,并將其返回,那么你可以跳過(guò)Web Form page框架,直接處理文件。如果你做的事情像從數(shù)據(jù)庫(kù)提供圖片,你也不需要Page框架—你不需要模板,而且沒(méi)有一個(gè)UI。沒(méi)有理由創(chuàng)建一個(gè)頁(yè)面對(duì)象和Seesion并且處理頁(yè)面級(jí)別的事件.

所以handlers更加高效。Handler也可以完成WebForm不能完成的任務(wù)。例如,他能夠處理一個(gè)請(qǐng)求,不需要磁盤(pán)上有物理文件。 做這個(gè),你需要在圖1中的應(yīng)用程序擴(kuò)展對(duì)話框中。
關(guān)閉“檢查文件是否存在”選項(xiàng)。
 
對(duì)于內(nèi)容提供者是通用的,就像動(dòng)態(tài)圖片處理,XML服務(wù)器,Url重定向提供構(gòu)造的Url,下載管理等等,這些都不是適合Wenform引擎。
對(duì)你來(lái)說(shuō),我介紹的足夠了么?
恩,我們這里已經(jīng)介紹了處理整個(gè)請(qǐng)求的過(guò)程。有很多的底層信息,我沒(méi)有仔細(xì)的講HTTPHandler和HTTPModule具體工作細(xì)節(jié)。挖掘這些信息需要一些時(shí)間,在理解ASP.NET怎樣工作上面,希望能給你和我自己一樣的滿意程度。
 
在結(jié)束之前,讓我們簡(jiǎn)短的回顧一下從IIS到handler的事件序列:
•IIS獲得請(qǐng)求
•檢測(cè)腳本映射,映射到ASP.NET_isapi.dll
•觸發(fā)工作進(jìn)程(ASP.NET_wp.exe 在 IIS5 或者 w3wp.exe 在 IIS6)
•.NET運(yùn)行時(shí)加載
•IsapiRuntime.ProcessRequest()通過(guò)非托管代碼調(diào)用
•IsapiWorkerRequest created once per request
•IsapiWorkerRequest 每一次請(qǐng)求創(chuàng)建一次
•HttpRuntime.ProcessRequest() called with Worker Request
•通過(guò)傳進(jìn)Work Request, HttpContext對(duì)象被創(chuàng)建
•HttpApplication.GetApplicationInstance() called with Context to retrieve instance from pool
•HttpApplication.Init() 調(diào)用,并且啟動(dòng)管道事件序列,附加Modules和Handler
•被調(diào)用,開(kāi)始處理進(jìn)請(qǐng)求
•管道事件觸發(fā)
•Handlers被調(diào)用,并且ProcessRequest 方法執(zhí)行
•控件返回管道并且發(fā)送請(qǐng)求事件觸發(fā)
 
通過(guò)這個(gè)簡(jiǎn)單的列表,把這些是如何組合起來(lái)的記住會(huì)更容易。我不時(shí)的來(lái)看它來(lái)記憶?,F(xiàn)在,我們回到工作上,繼續(xù)做一些不抽象的…
盡管,這里我說(shuō)的是基于ASP.NET1.1,但是ASP.NET2.0中,并沒(méi)有改變這些底層的處理過(guò)程。
非常感謝微軟的Mike Volodarsky來(lái)審閱這篇文章,并且提了一些附件的提示并且 Michele Leroux Bustamante提供了ASP.NET管道請(qǐng)求基礎(chǔ)信息。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多