ASP.NET Framework深度曆險
www.chinacs.net 2002-2-15 中文C#技術站
記得前一段時間有本不錯的書叫Delphi深度曆險,寫得不錯,我也就暫且借用了,:)
在這裡我不打算簡單介紹ASP.NET的入門知識了,ASP.NET除了名字和古老的ASP有些相同外,已經是完完全全的改變了,雖然你仍能在ASP.NET中發現你熟悉的Session,Application等等,但是不要嘗試将他們同遠古的ASP時代的Session等等畫上等号。
我們來慢慢的深入到ASP.NET Framework的核心内部,看看她是如何實作的,看看她是如何能承擔起下一代Web開發技術平台這個美譽的。
這篇東東不曾想過要完成多少章節,也沒有這個必要,權當日記的形式存在,或許很短,或許很長,我會盡我的所能來将ASP.NET Framework展現在諸位面前。
如果你對ASP.NET Framework沒有任何了解,你同樣可以成為ASP.NET coding高手,如果是這樣,你就不必繼續看下去了。
Chapter One -- Process a http request.
我們瞧一瞧ASP.NET Framework的運作機制和架構。
在開始之前,我們先跟随考古學家參觀一下古老的ASP運作機制:
當你請求一個*.asp檔案的時候,這個http request首先被inetinfo.exe程序所截獲,這個inetinfo.exe程序就是WWW服務程序,然後她會将這個請求轉交給asp.dll程序,asp.dll程序就會解釋執行這個asp葉面,然後将解釋後的資料流傳回給用戶端浏覽器。
轉過頭來我們看看如今的ASP.NET Framework是如何處理一個http request.
當你請求一個*.aspx檔案的時候,同樣的這個http request會被inetinfo.exe程序截獲,她判斷檔案的字尾之後,将這個請求轉交給ASPNET_ISAPI.dll,ASPNET_ISAPI.dll會通過一個被稱為Http PipeLine的管道,将請求發送給ASPNET_WP.exe程序,當這個http request進入ASPNET_WP.exe程序之後,會通過HttpRuntime來處理這個請求,處理完畢将結果傳回用戶端。
OK,好像并沒有太大的改進嘛,不要着急,在ASP.NET Framework中我們甚至能夠了解到HttpRuntime的細節。好,繼續深入下去:
當Http Request進入HttpRuntime之後,會繼續進入到一個被稱之為HttpApplication Factory的一個Container中,她會給出一個HttpApplication來處理傳遞進來的請求,這個請求會依次進入如下幾個Container:HttpModule->HttpHandler Factory->HttpHandler。
當系統内部的HttpHandler的ProcessResquest方法處理完畢之後,整個Http Request就完成了,用戶端也就得到相應的東東了。
整理一下ASP.NET Framework處理一個Http Request的流程:
HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->Http Pipeline-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()
或許會問,我知道這個處理流程有什麼用處呢?當然有用了,比如如果你想要中途截獲一個Http Request并且做些自己的處理,該如何做呢?這是下一次我們探讨的東東了,下次我們詳細讨論處理的細節問題。
see you later
(待續,歡迎探讨:[email protected])
.NET Framework Version:1.0.3705正式版
VS.NET(C#) Version:7.0.9466正式版
剛剛吃完晚飯,正好在鍵盤上面鍛煉一下手指。
接着上回繼續寫這個“日記”:
Chapter Two -- HttpModule是如何工作的?
我們上回說到,一個來自于用戶端的Http Request被截獲後經過層層轉交(怎麼都在踢皮球?呵呵)到達了HttpModule這個“請求監聽器”。
HttpModule就類似于安插在ASPNET_WP.EXE程序中的一個竊聽器,稍微有些常識的人都會很自然的想象得到竊聽器是用來做什麼的,而我們的HttpModule
可以說是作竊聽器的絕好人選了,但是需要明确的是,HttpModule絕對不是簡單的監聽器,它可以做到更多的東西,比如它可以對截獲的請求增加一些内容
等等。
另外需要明白的是,當一個Http Request到達HttpModule的時候,整個ASP.NET Framework系統還并沒有對這個請求做任何的真正處理,但是我們可以
在這個Http Request傳遞到真正的請求進行中心(HttpHandler)之前附加一些我們需要的資訊在這個Http Request至上,或者針對我們截獲的這個Http
Request資訊作一些額外的工作,或者在某些情況下幹脆終止滿足一些條件的Http Request,進而可以起到一個Filter過濾器的作用,而不僅僅是一個竊聽器了。
通過查閱MSDN(不要去相信.NET SDK自帶的那個QuickStarts Web文檔,正式版本中竟然在很多地方沒有更新這個文檔,很多東西在正式版本是無效的),
你會發現系統HttpModule實作了一個叫做IHttpModule的接口,很自然的就應當想到,隻要我們自己的類能夠實作IHttpModule接口,不就可以完全替代系統的
HttpModule了嗎?完全正确。
在我們開始自己的HttpModule類之前,我先來告訴你系統中的那個HttpModule是什麼樣子的,ASP.NET系統中預設的HttpModule有以下幾個:
System.Web.Caching.OutputCacheModule
System.Web.SessionState.SessionStateModule
System.Web.Security.WindowsAuthenticationModule
System.Web.Security.FormsAuthenticationModule
System.Web.Security.PassportAuthenticationModule
System.Web.Security.UrlAuthorizationModule
System.Web.Security.FileAuthorizationModule
好了,我們來開始我們自己的HttpModule建構曆程吧。
1)打開VS.NET建立一個“Class Library”項目,将它命名為MyHttpModule。
2)引用System.Web.dll檔案
在代碼區域敲入:
using System;
using System.Web;
namespace MyHttpModuleTest
{
/// <summary>
/// 說明:用來實作自定義HttpModule的類
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
public class MyHttpModule:IHttpModule
{
/// <summary>
/// 說明:構造器方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
public MyHttpModule()
{
}
/// <summary>
/// 說明:實作IHttpModule接口的Init方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
/// <param name="application">HttpApplication類型的參數</param>
public void Init(HttpApplication application)
{
application.BeginRequest +=new EventHandler(this.Application_BeginRequest);
application.EndRequest +=new EventHandler(this.Application_EndRequest);
}
/// <summary>
/// 說明:自己定義的用來做點事情的私有方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
/// <param name="obj">傳遞進來的對象參數</param>
/// <param name="e">事件參數</param>
private void Application_BeginRequest(Object obj,EventArgs e)
{
HttpApplication application=(HttpApplication)obj;
HttpContext context=application.Context;
HttpResponse response=context.Response;
HttpRequest request=context.Request;
response.Write("我來自Application_BeginRequest,:)");
}
/// <summary>
/// 說明:自己定義的用來做點事情的私有方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
/// <param name="obj">傳遞進來的對象參數</param>
/// <param name="e">事件參數</param>
private void Application_EndRequest(Object obj,EventArgs e)
{
HttpApplication application=(HttpApplication)obj;
HttpContext context=application.Context;
HttpResponse response=context.Response;
HttpRequest request=context.Request;
response.Write("我來自Application_EndRequest,:)");
}
/// <summary>
/// 說明:實作IHttpModule接口的Dispose方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
public void Dispose(){}
}
}
3)在VS.NET中編譯之後,你會得到MyHttpModule.dll這個檔案。
4)接下來我們的工作就是如何讓ASPNET_WP.exe程序将http request交給我們自己寫的這個HttpModule呢?方法就是配置web.config檔案。
在web.config檔案中增加如下幾句話:
<httpModules>
<add name="test" type="MyHttpModuleTest.MyHttpModule,MyHttpModule"/>
</httpModules>
注意要區分大小寫,因為web.config作為一個XML檔案是大小寫敏感的。“type=MyHttpModuleTest.MyHttpModule,MyHttpModule”告訴我們
系統将會将http request請求交給位于MyHttpModule.dll檔案中的MyHttpModuleTest.MyHttpModule類去處理。而這個DLL檔案系統将會自動
到/bin子目錄或者系統全局程式集緩沖區(GAC)搜尋。我們可以将我們剛才得到的DLL檔案放在bin子目錄中,至于後者,你可以通過.NET SDK正式版
自帶的Config工具做到,我們不詳細說了。
好了,我們的用來截獲http request請求的HttpModule就完成并且裝配完成了,你可以試着在你的web項目中建立一個新的WebForm,運作看看呢?:)
最後,我們假設一個使用這個HttpModule的場合。A站點提供免費的ASP.NET虛拟空間給大家,但是A站點的管理者并不想提供免費的午餐,他想要在每一個
頁面被浏覽的時候自動彈出自己公司的廣告(就像現在的www.X63.com一樣),我總不能時刻監視所有使用者的所有頁面吧,并且想要在每一個頁面手動添加
一段JS代碼,工作量是不可想象的,也是不現實的。那末好了,隻要我們的HttpModule一旦被挂接完成,這一切都将是輕而易舉的事情了,隻要我們在每一個
Http Request被我們捕獲的時候,給他增加上一些JS就好了!
我們上面提到在Init()方法中使用了兩個事件BeginRequest和EndRequest,這兩個事件分别是Init()中可以處理的所有事件的最開始事件和最終事件,在他們
中間還有一些其它的事件可以被我們利用,可以查閱MSDN。
另外在我關閉EditPlus之前,需要敲下如下的話:
在HttpModule中可以正常使用Response,Request,Server,Application,但是不能操作任何與Session有關代碼!
為什麼呢?自己考慮一下吧,下回看看原因在哪裡,另外再給出一個問題,你能發現系統預設的那幾個HttpModule在哪裡配置的呢?找找看。
下回我們看看HttpHandler部分以及如何同HttpModule相配合的東東。
See you later.
(待續,歡迎探讨:[email protected])
Author:uestc95
ArticleType:原創
E-mail:[email protected]
.NET Framework Version:1.0.3705正式版
VS.NET(C#) Version:7.0.9466正式版
這幾天胃口還算好,雖然算不上“吃嘛嘛香”,但是也算是不錯了,但願能增上幾斤才好。
怎麼樣,我們在Chapter Two最後提出的兩個問題估計早出來了吧,:)
First:為什麼在HttpModule中不能使用Session?
Second:系統預設的幾個HttpModule在哪裡配置的?
我們先挑軟柿子捏,第二個問題的答案是:在檔案machine.config中配置,比如在你的系統檔案目錄中的C:/WI
NNT/Microsoft.NET/Framework/v1.0.3705/CONFIG/machine.config。
雖然是個軟柿子,但是還是有些東東在裡面的,那就是machine.config和我們常見的web.config有什麼關
系呢?在ASP.NET Framework啟動處理一個Http Request的時候,她會依次加載machine.config以及你請求頁面
所在目錄的web.config檔案,裡面的配置是有<remove>标簽的,什麼意思不說也知道了吧。如果你在machine.c
onfig中配置了一個自己的HttpModule,你仍然可以在離你最近web.config檔案中“remove”掉這個映射關系。
至于第一個問題,呵呵,如果你仔細的運作過上次的檔案但是沒有自己仔細深入研究一下的話,一定會覺
得在HttpModule中的确無法使用Session,:)。如果你發現上次提出的問題本身就是一個"Problem",那麼恭喜你,你沒有掉進我故意給出的框框中,并且很有質疑精神,:)
今天我們就來解釋一下HttpModule和HttpHandler之間的關系,在今天的“日記”完成的時候,你也就會發現第一個問題的答案了。
Chapter Three -- 深入HttpModule
我們曾經提及當一個Http Request被ASP.NET Framework捕獲之後會依次交給HttpModule以及HttpHandler來處理,但是不能了解為HttpModule和HttpHandler是完全獨立的,實際上是,在Http Request在HttpModule傳遞的過程中會在某個事件内将控制權交給HttpHandler的,而真正的處理在HttpHandler中完成之後,再将控制權交還給HttpModule。也就是說HttpModule在某個請求經過她的時候會再恰當時候同HttpHandler進行通信,在何時,如何通信呢?這就是下面提到的了。
我們提到在HttpModule中最開始的事件是BeginRequest,最終的事件是EndRequest。你如果仔細看上次給出的源程式的話,應當發現在方法Init()中參數我們傳遞的是一個HttpApplication類型,而我們曾經提及的兩個事件正是這個傳遞進來的HttpApplication的事件之一。
HttpApplication還有其它的事件,分别如下:
application.BeginRequest
application.EndRequest
application.PreRequestHandlerExecute
application.PostRequestHandlerExecute
application.ReleaseRequestState
application.AcquireRequestState
application.AuthenticateRequest
application.AuthorizeRequest
application.ResolveRequestCache
application.PreSendRequestHeaders
application.PreSendRequestContent
需要注意的是,在事件EndRequest之後還會繼續執行application.PreSendRequestHeaders以及application.PreSendRequestContent事件,而這兩個事件大家想必應當從名稱上面看得出來事做什麼用途的了吧。是的,一旦觸發了這兩個事件,就表明整個Http Request的處理已經完成了,在這兩個事件中是開始向用戶端傳送處理完成的資料流了。看到這裡,您應當有一個疑問才對:怎麼沒見到HttpHandler就處理完成了?不是提到過HttpHandler才是真正處理Http Request的嗎?如果你有這個疑問表明你是仔細在看,也不枉我打字打得這莫累,:)。
其實一開始我就提到了,在一個http request在HttpModule傳遞過程中,會在某一個時刻(确切的說應當是事件)中将這個請求傳遞給HttpHandler的。這個事件就是ResolveRequestCache,在這個事件之後,HttpModule會建立一個HttpHandler的入口執行個體(做好準備了,:)),但是此時并沒有将控制權交出,而是繼續觸發AcquireRequestState以及PreRequestHandlerExecute事件(如果你實作了的話)。看到了嗎,最後一個事件的字首是Pre,呵呵。這表明下一步就要進入HttpHandler了,的确如此,正如我們猜想的那樣,在PreRequestHandlerExecute事件之後,HttpModule就會将控制權暫時交給HttpHandler,以便進行真正的http request處理工作。而在HttpHandler内部會執行ProcessRequest來處理請求。在HttpHandler處理完畢之後,會将控制權交還給HttpModule,HttpModule便會繼續對處理完畢的http Request進行層層的轉交動作,直到傳回到用戶端。
怎麼樣,是不是有些混亂?呵呵,苦于純文字無法畫流程圖,我手頭上已經畫好了一個整個HttpModule的生命周期圖,我隻能暫且在這裡用字元描繪一下前後流程了,如果你想要這個圖檔,可以給我發送mail,我給你mail過去。
Http Request在整個HttpModule中的生命周期圖:
Http Request開始
|
HttpModule
|
HttpModule.BeginRequest()
|
HttpModule.AuthenticateRequest()
|
HttpModule.AuthorizeRequest()
|
HttpModule.ResolveRequestCache()
|
建立HttpHandler控制點
|
接着處理(HttpHandler已經建立,此後Session可用)
|
HttpModule.AcquireRequestState()
|
HttpModule.PreRequestHandlerExecute()
|
進入HttpHandler處理HttpRequest
|
HttpHandler.ProcessRequest()
|
傳回到HttpModule接着處理(HttpHandler生命周期結束,Session失效)
|
HttpModule.PostRequestHandlerExecute()
|
HttpModule.ReleaseRequestState()
|
HttpModule.UpdateRequestCache()
|
HttpModule.EndRequest()
|
HttpModule.PreSendRequestHeaders()
|
HttpModule.PreSendRequestContent()
|
将處理後的資料傳回用戶端
|
整個Http Request處理結束
怎麼樣,從上面的圖中應當找到上次我們提出的第一個問題的答案了吧。
為了驗證上面的流程,我們可以用下面的這個自己的HttpModuel來驗證一下就知道了。
注意我們下面給出的是類的内容,架構還是前次我們給出的那個,自己加上就好了:
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
application.EndRequest += (new EventHandler(this.Application_EndRequest));
application.PreRequestHandlerExecute +=(new EventHandler(this.Application_PreRequestHandlerExecute));
application.PostRequestHandlerExecute +=(new EventHandler(this.Application_PostRequestHandlerExecute));
application.ReleaseRequestState +=(new EventHandler(this.Application_ReleaseRequestState));
application.AcquireRequestState +=(new EventHandler(this.Application_AcquireRequestState));
application.AuthenticateRequest +=(new EventHandler(this.Application_AuthenticateRequest));
application.AuthorizeRequest +=(new EventHandler(this.Application_AuthorizeRequest));
application.ResolveRequestCache +=(new EventHandler(this.Application_ResolveRequestCache));
application.PreSendRequestHeaders +=(new EventHandler(this.Application_PreSendRequestHeaders));
application.PreSendRequestContent +=(new EventHandler(this.Application_PreSendRequestContent));
}
private void Application_PreRequestHandlerExecute(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PreRequestHandlerExecute<br>");
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_BeginRequest<br>");
}
private void Application_EndRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_EndRequest<br>");
}
private void Application_PostRequestHandlerExecute(Object source,EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PostRequestHandlerExecute<br>");
}
private void Application_ReleaseRequestState(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_ReleaseRequestState<br>");
}
private void Application_UpdateRequestCache(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_UpdateRequestCache<br>");
}
private void Application_AuthenticateRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_AuthenticateRequest<br>");
}
private void Application_AuthorizeRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_AuthorizeRequest<br>");
}
private void Application_ResolveRequestCache(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_ResolveRequestCache<br>");
}
private void Application_AcquireRequestState(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_AcquireRequestState<br>");
}
private void Application_PreSendRequestHeaders(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PreSendRequestHeaders<br>");
}
private void Application_PreSendRequestContent(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PreSendRequestContent<br>");
}
public void Dispose()
{
}
好了,手累的不行了,:)
老規矩,下面的問題仔細考慮:
HttpModule中的Application的多個事件和Global.asax中的Application事件有聯系嗎?如果有,該會有哪些聯系呢?
下回會探讨HttpHandler的建構了,:)
不過最近挺忙,不知道何時能繼續......盡力吧。
See you later.
剛剛吃完晚飯,正好在鍵盤上面鍛煉一下手指。
接着上回繼續寫這個“日記”:
Chapter Two -- HttpModule是如何工作的?
我們上回說到,一個來自于用戶端的Http Request被截獲後經過層層轉交(怎麼都在踢皮球?呵呵)到達了HttpModule這個“請求監聽器”。
HttpModule就類似于安插在ASPNET_WP.EXE程序中的一個竊聽器,稍微有些常識的人都會很自然的想象得到竊聽器是用來做什麼的,而我們的HttpModule
可以說是作竊聽器的絕好人選了,但是需要明确的是,HttpModule絕對不是簡單的監聽器,它可以做到更多的東西,比如它可以對截獲的請求增加一些内容
等等。
另外需要明白的是,當一個Http Request到達HttpModule的時候,整個ASP.NET Framework系統還并沒有對這個請求做任何的真正處理,但是我們可以
在這個Http Request傳遞到真正的請求進行中心(HttpHandler)之前附加一些我們需要的資訊在這個Http Request至上,或者針對我們截獲的這個Http
Request資訊作一些額外的工作,或者在某些情況下幹脆終止滿足一些條件的Http Request,進而可以起到一個Filter過濾器的作用,而不僅僅是一個竊聽器了。
通過查閱MSDN(不要去相信.NET SDK自帶的那個QuickStarts Web文檔,正式版本中竟然在很多地方沒有更新這個文檔,很多東西在正式版本是無效的),
你會發現系統HttpModule實作了一個叫做IHttpModule的接口,很自然的就應當想到,隻要我們自己的類能夠實作IHttpModule接口,不就可以完全替代系統的
HttpModule了嗎?完全正确。
在我們開始自己的HttpModule類之前,我先來告訴你系統中的那個HttpModule是什麼樣子的,ASP.NET系統中預設的HttpModule有以下幾個:
System.Web.Caching.OutputCacheModule
System.Web.SessionState.SessionStateModule
System.Web.Security.WindowsAuthenticationModule
System.Web.Security.FormsAuthenticationModule
System.Web.Security.PassportAuthenticationModule
System.Web.Security.UrlAuthorizationModule
System.Web.Security.FileAuthorizationModule
好了,我們來開始我們自己的HttpModule建構曆程吧。
1)打開VS.NET建立一個“Class Library”項目,将它命名為MyHttpModule。
2)引用System.Web.dll檔案
在代碼區域敲入:
using System;
using System.Web;
namespace MyHttpModuleTest
{
/// <summary>
/// 說明:用來實作自定義HttpModule的類
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
public class MyHttpModule:IHttpModule
{
/// <summary>
/// 說明:構造器方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
public MyHttpModule()
{
}
/// <summary>
/// 說明:實作IHttpModule接口的Init方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
/// <param name="application">HttpApplication類型的參數</param>
public void Init(HttpApplication application)
{
application.BeginRequest +=new EventHandler(this.Application_BeginRequest);
application.EndRequest +=new EventHandler(this.Application_EndRequest);
}
/// <summary>
/// 說明:自己定義的用來做點事情的私有方法
/// 作者:uestc95
/// 聯系:ues[email protected]
/// </summary>
/// <param name="obj">傳遞進來的對象參數</param>
/// <param name="e">事件參數</param>
private void Application_BeginRequest(Object obj,EventArgs e)
{
HttpApplication application=(HttpApplication)obj;
HttpContext context=application.Context;
HttpResponse response=context.Response;
HttpRequest request=context.Request;
response.Write("我來自Application_BeginRequest,:)");
}
/// <summary>
/// 說明:自己定義的用來做點事情的私有方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
/// <param name="obj">傳遞進來的對象參數</param>
/// <param name="e">事件參數</param>
private void Application_EndRequest(Object obj,EventArgs e)
{
HttpApplication application=(HttpApplication)obj;
HttpContext context=application.Context;
HttpResponse response=context.Response;
HttpRequest request=context.Request;
response.Write("我來自Application_EndRequest,:)");
}
/// <summary>
/// 說明:實作IHttpModule接口的Dispose方法
/// 作者:uestc95
/// 聯系:[email protected]
/// </summary>
public void Dispose(){}
}
}
3)在VS.NET中編譯之後,你會得到MyHttpModule.dll這個檔案。
4)接下來我們的工作就是如何讓ASPNET_WP.exe程序将http request交給我們自己寫的這個HttpModule呢?方法就是配置web.config檔案。
在web.config檔案中增加如下幾句話:
<httpModules>
<add name="test" type="MyHttpModuleTest.MyHttpModule,MyHttpModule"/>
</httpModules>
注意要區分大小寫,因為web.config作為一個XML檔案是大小寫敏感的。“type=MyHttpModuleTest.MyHttpModule,MyHttpModule”告訴我們
系統将會将http request請求交給位于MyHttpModule.dll檔案中的MyHttpModuleTest.MyHttpModule類去處理。而這個DLL檔案系統将會自動
到/bin子目錄或者系統全局程式集緩沖區(GAC)搜尋。我們可以将我們剛才得到的DLL檔案放在bin子目錄中,至于後者,你可以通過.NET SDK正式版
自帶的Config工具做到,我們不詳細說了。
好了,我們的用來截獲http request請求的HttpModule就完成并且裝配完成了,你可以試着在你的web項目中建立一個新的WebForm,運作看看呢?:)
最後,我們假設一個使用這個HttpModule的場合。A站點提供免費的ASP.NET虛拟空間給大家,但是A站點的管理者并不想提供免費的午餐,他想要在每一個
頁面被浏覽的時候自動彈出自己公司的廣告(就像現在的www.X63.com一樣),我總不能時刻監視所有使用者的所有頁面吧,并且想要在每一個頁面手動添加
一段JS代碼,工作量是不可想象的,也是不現實的。那末好了,隻要我們的HttpModule一旦被挂接完成,這一切都将是輕而易舉的事情了,隻要我們在每一個
Http Request被我們捕獲的時候,給他增加上一些JS就好了!
我們上面提到在Init()方法中使用了兩個事件BeginRequest和EndRequest,這兩個事件分别是Init()中可以處理的所有事件的最開始事件和最終事件,在他們
中間還有一些其它的事件可以被我們利用,可以查閱MSDN。
另外在我關閉EditPlus之前,需要敲下如下的話:
在HttpModule中可以正常使用Response,Request,Server,Application,但是不能操作任何與Session有關代碼!
為什麼呢?自己考慮一下吧,下回看看原因在哪裡,另外再給出一個問題,你能發現系統預設的那幾個HttpModule在哪裡配置的呢?找找看。
下回我們看看HttpHandler部分以及如何同HttpModule相配合的東東。
See you later.
這幾天胃口還算好,雖然算不上“吃嘛嘛香”,但是也算是不錯了,但願能增上幾斤才好。
怎麼樣,我們在Chapter Two最後提出的兩個問題估計早出來了吧,:)
First:為什麼在HttpModule中不能使用Session?
Second:系統預設的幾個HttpModule在哪裡配置的?
我們先挑軟柿子捏,第二個問題的答案是:在檔案machine.config中配置,比如在你的系統檔案目錄中的C:/WI
NNT/Microsoft.NET/Framework/v1.0.3705/CONFIG/machine.config。
雖然是個軟柿子,但是還是有些東東在裡面的,那就是machine.config和我們常見的web.config有什麼關
系呢?在ASP.NET Framework啟動處理一個Http Request的時候,她會依次加載machine.config以及你請求頁面
所在目錄的web.config檔案,裡面的配置是有<remove>标簽的,什麼意思不說也知道了吧。如果你在machine.c
onfig中配置了一個自己的HttpModule,你仍然可以在離你最近web.config檔案中“remove”掉這個映射關系。
至于第一個問題,呵呵,如果你仔細的運作過上次的檔案但是沒有自己仔細深入研究一下的話,一定會覺
得在HttpModule中的确無法使用Session,:)。如果你發現上次提出的問題本身就是一個"Problem",那麼恭喜你,你沒有掉進我故意給出的框框中,并且很有質疑精神,:)
今天我們就來解釋一下HttpModule和HttpHandler之間的關系,在今天的“日記”完成的時候,你也就會發現第一個問題的答案了。
Chapter Three -- 深入HttpModule
我們曾經提及當一個Http Request被ASP.NET Framework捕獲之後會依次交給HttpModule以及HttpHandler來處理,但是不能了解為HttpModule和HttpHandler是完全獨立的,實際上是,在Http Request在HttpModule傳遞的過程中會在某個事件内将控制權交給HttpHandler的,而真正的處理在HttpHandler中完成之後,再将控制權交還給HttpModule。也就是說HttpModule在某個請求經過她的時候會再恰當時候同HttpHandler進行通信,在何時,如何通信呢?這就是下面提到的了。
我們提到在HttpModule中最開始的事件是BeginRequest,最終的事件是EndRequest。你如果仔細看上次給出的源程式的話,應當發現在方法Init()中參數我們傳遞的是一個HttpApplication類型,而我們曾經提及的兩個事件正是這個傳遞進來的HttpApplication的事件之一。
HttpApplication還有其它的事件,分别如下:
application.BeginRequest
application.EndRequest
application.PreRequestHandlerExecute
application.PostRequestHandlerExecute
application.ReleaseRequestState
application.AcquireRequestState
application.AuthenticateRequest
application.AuthorizeRequest
application.ResolveRequestCache
application.PreSendRequestHeaders
application.PreSendRequestContent
需要注意的是,在事件EndRequest之後還會繼續執行application.PreSendRequestHeaders以及application.PreSendRequestContent事件,而這兩個事件大家想必應當從名稱上面看得出來事做什麼用途的了吧。是的,一旦觸發了這兩個事件,就表明整個Http Request的處理已經完成了,在這兩個事件中是開始向用戶端傳送處理完成的資料流了。看到這裡,您應當有一個疑問才對:怎麼沒見到HttpHandler就處理完成了?不是提到過HttpHandler才是真正處理Http Request的嗎?如果你有這個疑問表明你是仔細在看,也不枉我打字打得這莫累,:)。
其實一開始我就提到了,在一個http request在HttpModule傳遞過程中,會在某一個時刻(确切的說應當是事件)中将這個請求傳遞給HttpHandler的。這個事件就是ResolveRequestCache,在這個事件之後,HttpModule會建立一個HttpHandler的入口執行個體(做好準備了,:)),但是此時并沒有将控制權交出,而是繼續觸發AcquireRequestState以及PreRequestHandlerExecute事件(如果你實作了的話)。看到了嗎,最後一個事件的字首是Pre,呵呵。這表明下一步就要進入HttpHandler了,的确如此,正如我們猜想的那樣,在PreRequestHandlerExecute事件之後,HttpModule就會将控制權暫時交給HttpHandler,以便進行真正的http request處理工作。而在HttpHandler内部會執行ProcessRequest來處理請求。在HttpHandler處理完畢之後,會将控制權交還給HttpModule,HttpModule便會繼續對處理完畢的http Request進行層層的轉交動作,直到傳回到用戶端。
怎麼樣,是不是有些混亂?呵呵,苦于純文字無法畫流程圖,我手頭上已經畫好了一個整個HttpModule的生命周期圖,我隻能暫且在這裡用字元描繪一下前後流程了,如果你想要這個圖檔,可以給我發送mail,我給你mail過去。
Http Request在整個HttpModule中的生命周期圖:
Http Request開始
|
HttpModule
|
HttpModule.BeginRequest()
|
HttpModule.AuthenticateRequest()
|
HttpModule.AuthorizeRequest()
|
HttpModule.ResolveRequestCache()
|
建立HttpHandler控制點
|
接着處理(HttpHandler已經建立,此後Session可用)
|
HttpModule.AcquireRequestState()
|
HttpModule.PreRequestHandlerExecute()
|
進入HttpHandler處理HttpRequest
|
HttpHandler.ProcessRequest()
|
傳回到HttpModule接着處理(HttpHandler生命周期結束,Session失效)
|
HttpModule.PostRequestHandlerExecute()
|
HttpModule.ReleaseRequestState()
|
HttpModule.UpdateRequestCache()
|
HttpModule.EndRequest()
|
HttpModule.PreSendRequestHeaders()
|
HttpModule.PreSendRequestContent()
|
将處理後的資料傳回用戶端
|
整個Http Request處理結束
怎麼樣,從上面的圖中應當找到上次我們提出的第一個問題的答案了吧。
為了驗證上面的流程,我們可以用下面的這個自己的HttpModuel來驗證一下就知道了。
注意我們下面給出的是類的内容,架構還是前次我們給出的那個,自己加上就好了:
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
application.EndRequest += (new EventHandler(this.Application_EndRequest));
application.PreRequestHandlerExecute +=(new EventHandler(this.Application_PreRequestHandlerExecute));
application.PostRequestHandlerExecute +=(new EventHandler(this.Application_PostRequestHandlerExecute));
application.ReleaseRequestState +=(new EventHandler(this.Application_ReleaseRequestState));
application.AcquireRequestState +=(new EventHandler(this.Application_AcquireRequestState));
application.AuthenticateRequest +=(new EventHandler(this.Application_AuthenticateRequest));
application.AuthorizeRequest +=(new EventHandler(this.Application_AuthorizeRequest));
application.ResolveRequestCache +=(new EventHandler(this.Application_ResolveRequestCache));
application.PreSendRequestHeaders +=(new EventHandler(this.Application_PreSendRequestHeaders));
application.PreSendRequestContent +=(new EventHandler(this.Application_PreSendRequestContent));
}
private void Application_PreRequestHandlerExecute(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PreRequestHandlerExecute<br>");
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_BeginRequest<br>");
}
private void Application_EndRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_EndRequest<br>");
}
private void Application_PostRequestHandlerExecute(Object source,EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PostRequestHandlerExecute<br>");
}
private void Application_ReleaseRequestState(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_ReleaseRequestState<br>");
}
private void Application_UpdateRequestCache(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_UpdateRequestCache<br>");
}
private void Application_AuthenticateRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_AuthenticateRequest<br>");
}
private void Application_AuthorizeRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_AuthorizeRequest<br>");
}
private void Application_ResolveRequestCache(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_ResolveRequestCache<br>");
}
private void Application_AcquireRequestState(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_AcquireRequestState<br>");
}
private void Application_PreSendRequestHeaders(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PreSendRequestHeaders<br>");
}
private void Application_PreSendRequestContent(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("Application_PreSendRequestContent<br>");
}
public void Dispose()
{
}
好了,手累的不行了,:)
老規矩,下面的問題仔細考慮:
HttpModule中的Application的多個事件和Global.asax中的Application事件有聯系嗎?如果有,該會有哪些聯系呢?