【转】Asp.net的生命周期应用之IHttpModule和IHttpHandler
引言
Http 請(qǐng)求處理流程?和?Http Handler 介紹?這兩篇文章里,我們首先了解了Http請(qǐng)求在服務(wù)器端的處理流程,隨后我們知道Http請(qǐng)求最終會(huì)由實(shí)現(xiàn)了IHttpHandler接口的類進(jìn)行處理(應(yīng)該記得Page類實(shí)現(xiàn)了IHttpHandler)。從?Http 請(qǐng)求處理流程?一文的最后的一幅圖中可以看到,在Http請(qǐng)求由IHttpHandler處理之前,它需要通過(guò)一系列的Http Module;在請(qǐng)求處理之后,它需要再次通過(guò)一系列的Http Module,那么這些Http Module是如何組成的?用來(lái)做什么呢?本文將對(duì)Http Module作以介紹。
Http Module概述
暫時(shí)先不考慮我們自己實(shí)現(xiàn)Http Module的情況。在.Net中,Http Module 是實(shí)現(xiàn)了IHttpModule接口的程序集。IHttpModule 接口本身并沒(méi)有什么好大寫特寫的,由它的名字可以看出,它不過(guò)是一個(gè)普普通通的接口而已。實(shí)際上,我們關(guān)心的是實(shí)現(xiàn)了這些接口的類,如果我們也編寫代碼實(shí)現(xiàn)了這個(gè)接口,那么有什么用途。一般來(lái)說(shuō),我們可以將Asp.Net中的事件分成三個(gè)級(jí)別,最頂層是 應(yīng)用程序級(jí)事件、其次是頁(yè)面級(jí)事件、最下面是控件級(jí)事件,事件的觸發(fā)分別與 應(yīng)用程序周期、頁(yè)面周期、控件周期緊密相關(guān)。而 Http Module 的作用是與應(yīng)用程序事件 密切相關(guān)的。
我們通過(guò)Http Module在Http請(qǐng)求管道(Pipeline)中注冊(cè)期望對(duì)應(yīng)用程序事件做出反應(yīng)的方法,在相應(yīng)的事件觸發(fā)的時(shí)候(比如說(shuō)BeginRequest事件,它在應(yīng)用程序收到一個(gè)Http請(qǐng)求并即將對(duì)其進(jìn)行處理時(shí)觸發(fā)),便會(huì)調(diào)用Http Module注冊(cè)了的方法,實(shí)際的工作在這些方法中執(zhí)行。.Net 本身已經(jīng)有很多的Http Module,其中包括 表單驗(yàn)證Module(FormsAuthenticationModule), Session 狀態(tài)Module(SessionStateModule),輸出緩存Module (OutputCacheModule)等。
注冊(cè) Http Module
在注冊(cè)我們自己編寫的 Http Module 之前,先來(lái)看看Asp.Net中已經(jīng)有的HttpModule。與 Http Handler類似,我們需要打開機(jī)器上C:\WINDOWS\Microsoft.NET\Framework\ v2.0.50727\CONFIG 目錄下的 web.config 文件。找到 <httpModules/> 結(jié)點(diǎn),應(yīng)該可以看到下面的內(nèi)容:
<httpModules>
??? <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
??? <add name="Session" type="System.Web.SessionState.SessionStateModule" />
??? <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
??? <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
??? <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
??? <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
??? <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
... 略
</httpModules>
我們先從結(jié)點(diǎn)上看,type屬性與上一節(jié)所說(shuō)的http handler結(jié)點(diǎn)的type屬性類似,都代表了相應(yīng)的程序集。但是,與http handler 不同,module只提供了一個(gè)name屬性,沒(méi)有諸如 path這樣指定某一特定(或者用通配符 * 代表某一種類)文件的處理程序。這是與Module的特點(diǎn)相關(guān)的,我們知道 module 是響應(yīng)應(yīng)用程序周期中觸發(fā)的事件,對(duì)于所有提交到aspnet_isapi.dll的請(qǐng)求都一樣,即便請(qǐng)求只是像類似http://www.tracefact.net/images/logo.gif 這樣獲取一張圖片而已(對(duì)ISAPI進(jìn)行過(guò)設(shè)置以后,默認(rèn)aspnet_isapi.dll不接手圖片文件)。
與Http handler類似,在這冊(cè)我們自己的http module 時(shí),假設(shè)類名為ModuleDemo,位于myNameSpace命名空間下,程序集名稱為myDll,我們只需將myDll.dll拷貝到Bin目錄下,并在站點(diǎn)的 web.config 文件 system.web 結(jié)點(diǎn)下創(chuàng)建 httpModules 結(jié)點(diǎn):
<system.web>
??? <httpModules>
?????? <add name="CustomModuleName" type="myNameSpace.ModuleDemo, myDll"/>
??? </httpModules>
</system.web>
type屬性由分號(hào)“,”分為兩部分,前面是命名空間及類名,也就是類型名;后面是程序集名。如果我們將代碼創(chuàng)建在App_Code目錄中,則不需要再指定程序集名。
name屬性由我們自己命名,不一定與類名相同,此處我將它命名為“CustomModuleName”。我們可以通過(guò)應(yīng)用程序(HttpApplication)的Modules屬性獲取HttpModuleCollection集合,然后通過(guò)name屬性,進(jìn)一步獲取HttpModule對(duì)象。
通過(guò)name屬性,我們還可以在global.asax中文件中編寫自定義HttpModule暴露出的事件的處理程序,它采用的格式是:void ModuleName_EventName(object sender, EventArgs e)。我們將在后面做更詳細(xì)介紹。
Asp.Net 內(nèi)置的 Http Modules
下面這張表格列出了C:\WINDOWS\Microsoft.NET\Framework\ v2.0.50727\CONFIG下的Web.Config中的 Asp.Net 內(nèi)置的Http Modules 及其主要作用。
| 名稱 | 類型 | 功能 |
| OutputCache | System.Web.Caching.OutputCacheModule | 頁(yè)面級(jí)輸出緩存 |
| Session | System.Web.SessionState.SessionStateModule | Session狀態(tài)管理 |
| WindowsAuthentication | System.Web.Security.WindowsAuthenticationModule | 用集成Windows身份驗(yàn)證進(jìn)行客戶端驗(yàn)證 |
| FormsAuthentication | System.Web.Security.FormsAuthenticationModule | 用基于Cookie的窗體身份驗(yàn)證進(jìn)行客戶端身份驗(yàn)證 |
| PassportAuthentication | System.Web.Security.PassportAuthenticationModule | 用MS護(hù)照進(jìn)行客戶身份驗(yàn)證 |
| RoleManager | System.Web.Security.RoleManagerModule | 管理當(dāng)前用戶角色 |
| UrlAuthorization | System.Web.Security.UrlAuthorizationModule | 判斷用戶是否被授權(quán)訪問(wèn)某一URL |
| FileAuthorization | System.Web.Security.FileAuthorizationModule | 判斷用戶是否被授權(quán)訪問(wèn)某一資源 |
| AnonymousIdentification | System.Web.Security.AnonymousIdentificationModule | 管理Asp.Net應(yīng)用程序中的匿名訪問(wèn) |
| Profile | System.Web.Profile.ProfileModule | 管理用戶檔案文件的創(chuàng)立 及相關(guān)事件 |
| ErrorHandlerModule | System.Web.Mobile.ErrorHandlerModule | 捕捉異常,格式化錯(cuò)誤提示字符,傳遞給客戶端程序 |
我們將在后面用編程的方式來(lái)查看它。
IHttpModule接口
看了這么多理論知識(shí),本節(jié)將開始動(dòng)手寫點(diǎn)程序,實(shí)現(xiàn)自己的Http Module。我們首先需要看下IHttpModule 接口,它包括下面兩個(gè)方法:
public void Init(HttpApplication context);
public void Dispose();
Init():這個(gè)方法接受一個(gè)HttpApplication對(duì)象,HttpApplication代表了當(dāng)前的應(yīng)用程序,我們需要在這個(gè)方法內(nèi)注冊(cè) HttpApplication對(duì)象暴露給客戶端的事件。可見,這個(gè)方法僅僅是用來(lái)對(duì)事件進(jìn)行注冊(cè),而實(shí)際的事件處理程序,需要我們另外寫方法。
整個(gè)過(guò)程很好理解:
NOTE:如果你不了解事件注冊(cè)等相關(guān)內(nèi)容,請(qǐng)參閱?C#中的委托與事件?一文。
Dispose():它可以在進(jìn)行垃圾回收之前進(jìn)行一些清理工作。
綜上所述:實(shí)現(xiàn)一個(gè) IHttpModule 的模板一般是這樣的:
public class ModuleDemo:IHttpModule
{
??? public void Init(HttpApplication context) {
?????? // 注冊(cè)HttpApplication應(yīng)用程序 BeginRequest 事件
?????? // 也可以是其他任何HttpApplication暴露出的事件
?????? context.BeginRequest += new EventHandler(context_BeginRequest);
??? }
??? void context_BeginRequest(object sender, EventArgs e) {
?????? HttpApplication application = (HttpApplication)sender;
?????? HttpContext context = application.Context;
?????? // 做些實(shí)際的工作,HttpContext對(duì)象都獲得了,剩下的基本可以自由發(fā)揮了
??? }
??? public void Dispose() {
??? }
}
通過(guò)Http Module向Http請(qǐng)求輸出流中寫入文字
本例中,我們僅用BeginRequest事件和 EndRequest 事件對(duì) Http Module 的使用作以說(shuō)明。我們通過(guò)這個(gè)范例,了解 Http Module 基本的使用方法。
首先,請(qǐng)創(chuàng)建一個(gè)新的站點(diǎn),在App_Code目錄中添加類文件: ModuleDemo.cs:
public class ModuleDemo:IHttpModule
{
??? // Init方法僅用于給期望的事件注冊(cè)方法
??? public void Init(HttpApplication context) {
?????? context.BeginRequest += new EventHandler(context_BeginRequest);
?????? context.EndRequest += new EventHandler(context_EndRequest);
??? }
??? // 處理BeginRequest 事件的實(shí)際代碼
??? void context_BeginRequest(object sender, EventArgs e) {
?????? HttpApplication application = (HttpApplication)sender;
?????? HttpContext context = application.Context;
?????? context.Response.Write("<h1 style='color:#00f'>來(lái)自HttpModule 的處理,請(qǐng)求到達(dá)</h1><hr>");
??? }
??? // 處理EndRequest 事件的實(shí)際代碼
??? void context_EndRequest(object sender, EventArgs e) {
?????? HttpApplication application = (HttpApplication)sender;
?????? HttpContext context = application.Context;
?????? context.Response.Write("<hr><h1 style='color:#f00'>來(lái)自HttpModule的處理,請(qǐng)求結(jié)束</h1>");
??? }
??????
??? public void Dispose() {
??? }
}
上面的代碼很簡(jiǎn)單,它注冊(cè)了 HttpApplication實(shí)例的 BeginRequest 事件 和 EndRequest事件,事件處理方法的作用僅僅是在http請(qǐng)求開始和結(jié)束的時(shí)候,給http請(qǐng)求的輸入流中分別寫入不同的內(nèi)容。
接下來(lái)在 Web.config 的 System.web 結(jié)點(diǎn)中寫入以下內(nèi)容:
<system.web>
??? <httpModules>
?????? <add name="MyModule" type="ModuleDemo" />
??? </httpModules>
</system.web>
然后,打開建立站點(diǎn)時(shí)自動(dòng)創(chuàng)建的 Default.aspx文件,在里面打幾個(gè)字,為了做區(qū)分,我輸入的是:位于.aspx頁(yè)面上的文字。然后,我們?cè)跒g覽器中打開它,應(yīng)該會(huì)看到像這樣:
然后我們?cè)傩陆ㄒ粋€(gè) Default2.aspx,在瀏覽器中瀏覽,可以看到,兩個(gè)頁(yè)面的效果相同。這說(shuō)明對(duì)于不同的兩個(gè)文件,http Module都起了作用,可見它確實(shí)是位于應(yīng)用程序級(jí),而非頁(yè)面級(jí)。
現(xiàn)在,我們?cè)俅蜷_站點(diǎn)中的一張圖片文件,發(fā)現(xiàn)顯示出的是一個(gè)紅叉叉,為什呢?因?yàn)镠ttp Module 針對(duì)是http 請(qǐng)求,而不是某個(gè)或某一類文件,所以當(dāng)請(qǐng)求一張圖片的時(shí)候,我們編寫的http Module依然會(huì)起作用,將文字插入到二進(jìn)制圖片中,破壞了文件格式,自然只能顯示紅叉叉了。
NOTE:如果你發(fā)現(xiàn)你的圖片顯示正常,請(qǐng)不要驚訝,事情是這樣的:回想一下第一節(jié)我們討論到的,對(duì)于圖片文件,由IIS直接處理,并不會(huì)交由aspnet_isapi.dll,所以,Module無(wú)法捕獲對(duì)于圖片類型文件的請(qǐng)求。解決方法就是在IIS中進(jìn)行設(shè)置一下。
??? 這里需要提請(qǐng)注意的是:如果你使用Vs2005自帶的Local Server,那么你無(wú)需對(duì)IIS進(jìn)行設(shè)置,所有的不論圖片還是任何文件類型,都會(huì)交由aspnet_isapi.dll處理。
遍歷Http Module集合
現(xiàn)在,我們通過(guò)遍歷 HttpModuleCollection 集合來(lái)查看注冊(cè)給應(yīng)用程序的所有 Http Module 的名稱。
新建一個(gè)文件 RegisteredModules.aspx,在代碼后置文件中添加如下方法:
private string ShowModules() {
??? HttpApplication app = Context.ApplicationInstance; //獲取當(dāng)前上下文的HttpApplication環(huán)境
??? HttpModuleCollection moduleCollection = app.Modules; //獲取所有Module集合
??? // 獲取所有的 Module 名稱
??? string[] moduleNames = moduleCollection.AllKeys;
??? System.Text.StringBuilder results = new System.Text.StringBuilder();??? //遍歷結(jié)果集
??? foreach (string name in moduleNames) {
?????? // 獲得Module名稱
?????? results.Append("<b style='color:#800800'>名稱:" + name + "</b><br />");
??? ??? // 獲得Module類型
?????? results.Append("類型:" + moduleCollection[name].ToString() + "<br />");
??? }
??? return results.ToString();
}
然后在Page_Load方法中輸出一下:
protected void Page_Load(object sender, EventArgs e)
{
??? Response.Write(ShowModules());
}
我們應(yīng)該可以看到下面這樣的畫面:
?
與之前列出的那張表格比較一下,可以看出是幾乎完全一致的(多了一個(gè)DefaultAuthentication)。另外注意上圖的倒數(shù)第四行,那不是我們自己定義的Module么?name為MyModule,類型為ModuleDemo。
Global.asax文件與 Http Module
早在asp時(shí)代,大家就知道這個(gè)文件了。它主要用于放置對(duì)于 應(yīng)用程序事件或者 Session事件的響應(yīng)程序。大家熟悉的有Application_Start、Application_End、Session_Start、Session_End 等。
在asp.net中,Glabal不僅可以注冊(cè)應(yīng)用程序和Session事件,還可以注冊(cè)Http Module暴露出的事件;不僅可以注冊(cè)系統(tǒng)Module的事件,也可以注冊(cè)我們自己義的Module暴露出的事件。在具體介紹之前,這里需要首先注意兩點(diǎn):
好了,我們現(xiàn)在修改之前 ModuleDemo 范例程序,給它像下面這樣給它添加一個(gè)事件(為了使程序簡(jiǎn)潔一些,我做了簡(jiǎn)化):
public class ModuleDemo : IHttpModule {
??? // 聲明一個(gè)事件
??? public event EventHandler ExposedEvent;
??? // Init方法僅用于給期望的事件注冊(cè)方法
??? public void Init(HttpApplication context) {
?????? context.BeginRequest += new EventHandler(context_BeginRequest);
??? }
??? // 處理BeginRequest 事件的實(shí)際代碼
??? void context_BeginRequest(object sender, EventArgs e) {
?????? HttpApplication application = (HttpApplication)sender;
?????? HttpContext context = application.Context;
?????? context.Response.Write("<h3 style='color:#00f'>來(lái)自HttpModule的處理,請(qǐng)求到達(dá)</h3><hr>");
??????
?????? OnExposedEvent(new EventArgs()); // 調(diào)用方法
??? }
??? protected override void OnExposedEvent(EventArgs e) {
?????? if (ExposedEvent != null) // 如果Global中有注冊(cè)
?????????? ExposedEvent(this, e);?? // 調(diào)用注冊(cè)了的方法
??? }
???
??? public void Dispose() {
??? }
}
接下來(lái),我們?cè)谡军c(diǎn)中創(chuàng)建一個(gè) Global.asax 文件,在里面添加如下代碼,注意到格式是:void 模塊名_事件名(object sender, EventArgs e)。
void MyModule_ExposedEvent(object sender, EventArgs e)
{
???? Response.Write("<h3 style='color:#800800'>來(lái)自 Global.asax 的文字</h2>");
}
現(xiàn)在,我們打開之前的頁(yè)面,應(yīng)該可以見到這樣,可見,我們成功的將 Glabal.asax文件與我們自己定義的Http Module所暴露出的事件 ExposedEvent 聯(lián)系到了一起:
?
總結(jié)
本文簡(jiǎn)單地介紹了什么是Http Module。我們首先了解了Http Module的作用,然后查看了Asp.Net 內(nèi)置的Module,接著我們介紹了IHttpModule接口,并通過(guò)了一個(gè)簡(jiǎn)單的范例實(shí)現(xiàn)了此接口,最后我們討論了 Http Module與 Global.asax 文件的聯(lián)系。
本文僅僅是對(duì)IHttpModule作以簡(jiǎn)單介紹,對(duì)其更多的實(shí)際應(yīng)用,會(huì)在后續(xù)文章中補(bǔ)充。
希望這篇文章能給你帶來(lái)幫助!
本文的源代碼下載:http://www.tracefact.net/sourcecode/Introduction-to-HttpModule.rar
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的【转】Asp.net的生命周期应用之IHttpModule和IHttpHandler的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《卧龙:苍天陨落》中配预告发布 简中官网
- 下一篇: Sharepoint学习笔记—ECM系列