asp.net 页面全生命周期
.Net 頁面生命周期?
2012-03-21 13:31:08|? 分類: .NET |? 標(biāo)簽:.net? 頁面生命周期? c#? httpapplication? |字號大中小訂閱
.Net 頁面生命周期 IIS 所收到的對某 Microsoft ASP.NET 頁面的每個請求都被移交給 ASP.NET HTTP 管線。HTTP 管線由一系列托管對象組成,這些對象按順序處理該請求,并完成從 URL 到普通 HTML 文本的轉(zhuǎn)換。HTTP 管線的入口點是 HttpRuntime 類。ASP.NET 基礎(chǔ)結(jié)構(gòu)為輔助進(jìn)程中所承載的每個 AppDomain (應(yīng)用程序域)創(chuàng)建此類的一個實例請注意,該輔助進(jìn)程為當(dāng)前正在運(yùn)行的每個 ASP.NET 應(yīng)用程序維護(hù)一個不同的 AppDomain。 要激活 HTTP 管道,可以創(chuàng)建一個 HttpRuntime 類的新實例,然后調(diào)用其 ProcessRequest 方法。一個完整的頁面請求會包括下面的流程: (1).被WWW服務(wù)器截獲(inetinfo.exe進(jìn)程), 該進(jìn)程首先判斷頁面后綴, 然后根據(jù)IIS中配置決定調(diào)用具體的擴(kuò)展程序。aspx就會調(diào)用aspnet_isapi.dll,(2)由aspnet_isapi.dll發(fā)送給w3wp.exe(iis 工作者進(jìn)程,IIS6.0中叫做 w3wq.exe,IIS5.0中叫做 aspnet_wp.exe)。 (3) 在w3wp.exe調(diào)用.NET類庫進(jìn)行具體處理,順序如下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler ISAPIRuntime:主要作用是調(diào)用一些非托管代碼生成HttpWorkerRequest對象,HttpWorkerRequest對象包含當(dāng)前請求的所有信息,然后傳遞給HttpRuntime
HttpRuntime:根據(jù)HttpWorkerRequest對象生成HttpContext,HttpContext包含request、 response等屬性, 再調(diào)用HttpApplicationFactory來生成IHttpHandler, 調(diào)用HttpApplication對象執(zhí)行請求
HttpApplicationFactory: 生成一個HttpApplication對象 HttpApplication:進(jìn)行HttpModule的初始化,HttpApplication創(chuàng)建針對此Http請求的 HttpContext對象
HttpModule: 當(dāng)一個HTTP請求到達(dá)HttpModule時,整個ASP.NET Framework系統(tǒng)還并沒有對這個HTTP請求做任何處理,也就是說此時對于HTTP請求來講,HttpModule是一個HTTP請求的“必經(jīng)之 路”,所以可以在這個HTTP請求傳遞到真正的請求處理中心(HttpHandler)之前附加一些需要的信息在這個HTTP請求信息之上,或者針對截獲 的這個HTTP請求信息作一些額外的工作,或者在某些情況下干脆終止?jié)M足一些條件的HTTP請求,從而可以起到一個Filter過濾器的作用。
HttpHandlerFactory:把用戶request 轉(zhuǎn)發(fā)到HttpHandlerFactory,再由HttpHandlerFactory實例化HttpHandler對象來相應(yīng)request HttpHandle:Http處理程序,處理頁面請求
///HttpRuntime主方法ProcessRequest(主要方法) ??? private void ProcessRequestInternal(HttpWorkerRequest wr) ??? { ??????? // 調(diào)用HttpWorkerRequest生成HttpContext ??????? HttpContext context = new HttpContext(wr, false); ??????? //傳入context,生成一個IHttpHandler實例 ??????? IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context); ??????? //調(diào)用IHttpHandler.ProcessRequest的方法 ??????? app.ProcessRequest(context); ??? } HttpApplication對象詳解 一旦請求上下文被創(chuàng)建,HttpRuntime類便會建立處理請求的ASP.NET應(yīng)用程序?qū)ο蟆SP.NET應(yīng)用程序由許多 HttpApplication類的實例組成。HttpApplication是根據(jù)global.asax文件派生的對象,能夠處理轉(zhuǎn)到特定虛擬文件夾的所有HTTP請求。運(yùn)行中的ASP.NET應(yīng)用程序完全由其虛擬文件夾表示的,且global.asax文件是可選的。虛擬文件夾的名稱是一種鍵,HTTP運(yùn)行庫通過它來確定應(yīng)該由哪個應(yīng)用程序來處理傳入的請求。
請求會被分配給某個HttpApplication對象,此后便由這個選定的對象來負(fù)責(zé)管理請求的整個生存期。只有在請求處理完成 后,HttpApplication對象才能被重新使用。如果沒有可用的HttpApplication對象,則將創(chuàng)建新的 HttpApplication對象,并被放入池中。
HttpApplication的19個標(biāo)準(zhǔn)事件 處理過程的簡單介紹 在Asp.net中,Asp.net服務(wù)器對于每一次請求的處理過程是相同的,都要經(jīng)過這個HttpApplication的處理管道。管道內(nèi)部的處理過程是固定的,在服務(wù)器處理請求的各個階段。伴隨著處理的進(jìn)行,依次觸發(fā)對應(yīng)的事件,以便于程序員在處理的各個階段完成自定義的處理工作。 開始處理請求后,第一個重要的工作就是確定請求用戶的身份以實現(xiàn)安全機(jī)制,這個工作通過AuthenticateRequest和PostAuthenticateRequest兩個事件提供檢查當(dāng)前請求用戶身份的機(jī)會,顯然,AuthenticateRequest表示開始檢查用戶的身份,而PostAuthenticateRequest則表示用戶身份已經(jīng)檢查完成,檢查后的用戶可以通過HttpContent的User屬性獲取到。這個屬性的類型為Syste.Security.Principal.IPrincipal,Iprincipal有一個名為Identity,類型為Syste.Security.Principal.IIdentity 的屬性,IIdentity有一個類型為bool型的屬性IsAuthenticated,表示當(dāng)前請求的用戶是否已經(jīng)被驗證,或者說確定了用戶是否是匿名用戶,IsAuthenticated如果為false,那么,表示這是一個匿名用戶,如果為true,那么通過IIdentity類型為string的Name屬性,這就是當(dāng)前請求的用戶名。 ?????? 當(dāng)Asp.Net獲取用戶的身份之后,根據(jù)當(dāng)前請求的用戶身份,開始請求權(quán)限的檢查工作。當(dāng)?shù)?個事件AuthorizeRequest出發(fā)的時候,表示開始進(jìn)行用戶的權(quán)限檢查,而第5個事件PostAuthorizeRequest則標(biāo)志著已經(jīng)完成了用戶權(quán)限的檢查工作。如果用戶沒有通過安全檢查,一般情況下,則跳過剩下的事件,直接觸發(fā)EndRequest事件結(jié)束請求的處理過程。 ????? 當(dāng)用戶獲取了請求的權(quán)限,那么服務(wù)器開始準(zhǔn)備用最快的方式來使用戶得到回應(yīng)的結(jié)果。ResolveRequestCache事件標(biāo)識著到從前緩存的結(jié)果中進(jìn)行檢查,看看是否可以直接從以前緩存的結(jié)果中直接取得處理的結(jié)果,PostResolveRequestCache表示緩存檢查的結(jié)束。 ?????? 當(dāng)不能從緩存中獲取結(jié)果的時候,必須通過一次處理來計算出當(dāng)前請求的結(jié)果,在Asp.Net中用于處理請求以得到結(jié)果的對象成為處理程序Handler,在Asp.net中提供了許多的處理程序,程序員也可以自定義處理程序。為了處理這個請求,Asp.net必須按照匹配規(guī)則找到一個處理當(dāng)前請求的處理程序,PostMapRequestHandler事件表示Asp.net已經(jīng)獲取了這個處理程序,HttpContent的Handler屬性就表示這個處理程序?qū)ο?#xff0c;從上面的分析可以看到,HttpContent的Handler屬性到這里才有實際的意義。 ????? 得到了處理程序之后,還不能馬上開始進(jìn)行處理,這是由于處理請求還需要許多與這個請求有關(guān)的數(shù)據(jù),比如說,這個用戶在上一次向服務(wù)器發(fā)請求的時候,在服務(wù)器上保存了一些這個用戶特有的數(shù)據(jù)。從ASP時代開始,Session這個概念就出現(xiàn)在web開發(fā)中,提供基于回話的狀態(tài)管理,由于Http協(xié)議的無狀態(tài)性,狀態(tài)管理的問題是web開發(fā)的一個核心問題。 ????? 為了獲取這個用戶在以前保存的專屬數(shù)據(jù),AcquireRequestStatue事件給程序員提供了一個切入點,PostAcquireRequestState事件則表示已經(jīng)完成了用戶數(shù)據(jù)的獲取工作,可以在處理中使用了。 ????? 萬事俱備,只欠東風(fēng),PreRequestHandlerExecute事件用來通知程序員,處理程序就要開始進(jìn)行處理工作了,如果在用戶的狀態(tài)已經(jīng)獲取之后,還有需要在處理程序處理之前進(jìn)行的工作,那么就在這個事件(PreRequestHandlerExecute)中處理吧 ?????? 在PreRequestHandlerExecute事件之后,asp.net服務(wù)器將通過執(zhí)行處理程序完成請求的處理工作。這個處理程序可能是一個web窗體,也可能是一個web服務(wù)。這個工作在第11個事件和第12個事件之間完成。 ?????? 處理程序完成之后,服務(wù)器開始進(jìn)行掃尾工作,PostRequestHandlerExecute事件通知程序員,asp.net服務(wù)器的處理程序已經(jīng)完成。 ?????? 在處理完成之后,由于在處理程序中,用戶可能修改了用戶特定的專屬數(shù)據(jù),那么,修改之后的用戶狀態(tài)數(shù)據(jù)可能需要進(jìn)行序列化或者進(jìn)行保存處理,ReleaseRequestState事件通知程序員需要釋放這些狀態(tài)數(shù)據(jù),PostReleaseRequestState則表示已經(jīng)釋放完成。 ?????? 在處理完成之后,如果希望將這次處理的結(jié)果緩存起來,以便于在后繼的請求中可以直接使用這個結(jié)果,UpdateRequestCache時間提供了處理的機(jī)會,PostUpdateRequestCache則表示緩存已經(jīng)更新完成。HttpModule與HttpHandler區(qū)別 http://blog.csdn.net/zhaili1978/article/details/6335595
ASP.NET對請求處理的過程:
當(dāng)請求一個*.aspx文件的時候,這個請求會被inetinfo.exe進(jìn)程截獲,它判斷文件的后綴(aspx)之后,將這個請求轉(zhuǎn)交給ASPNET_ISAPI.dll,ASPNET_ISAPI.dll會通過http管道(Http PipeLine)將請求發(fā)送給ASPNET_WP.exe進(jìn)程,在ASPNET_WP.exe進(jìn)程中通過HttpRuntime來處理這個請求,處理完畢將結(jié)果返回客戶端。
inetinfo.exe進(jìn)程:是www服務(wù)的進(jìn)程,IIS服務(wù)和ASPNET_ISAPI.DLL都寄存在此進(jìn)程中。
ASPNET_ISAPI.DLL:是處理.aspx文件的win32組件。其實IIS服務(wù)器是只能識別.html文件的,當(dāng)IIS服務(wù)器發(fā)現(xiàn)被請求的文件是.aspx文件時,IIS服務(wù)器將其交給aspnet_isapi.dll來處理。
aspnet_wp.exe進(jìn)程:ASP.NET框架進(jìn)程,提供.net運(yùn)行的托管環(huán)境,.net的CLR(公共語言運(yùn)行時)就是寄存在此進(jìn)程中。
ASP.NET Framework處理一個Http Request的流程:
HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()
ASP.NET請求處理過程是基于管道模型的,這個管道模型是由多個HttpModule和HttpHandler組成,ASP.NET把 http請求依次傳遞給管道中各個HttpModule,最終被HttpHandler處理,處理完成后,再次經(jīng)過管道中的HTTP模塊,把結(jié)果返回給客 戶端。我們可以在每個HttpModule中都可以干預(yù)請求的處理過程。
注意:在http請求的處理過程中,只能調(diào)用一個HttpHandler,但可以調(diào)用多個HttpModule。
當(dāng)請求到達(dá)HttpModule的時候,系統(tǒng)還沒有對這個請求真正處理,但是我們可以在這個請求傳遞到處理中心(HttpHandler)之前 附加一些其它信息,或者截獲的這個請求并作一些額外的工作,也或者終止請求等。在HttpHandler處理完請求之后,我們可以再在相應(yīng)的 HttpModule中把請求處理的結(jié)果進(jìn)行再次加工返回客戶端。
HttpModule
HTTP模塊是實現(xiàn)了System.Web.IhttpModule接口的類。
IHttpModule接口的聲明:
public interface IHttpModule
{
void Init (HttpApplication context);
void Dispose ();
}
Init 方法:系統(tǒng)初始化的時候自動調(diào)用,這個方法允許HTTP模塊向HttpApplication 對象中的事件注冊自己的事件處理程序。
Dispose方法: 這個方法給予HTTP模塊在對象被垃圾收集之前執(zhí)行清理的機(jī)會。此方法一般無需編寫代碼。
HTTP模塊可以向System.Web.HttpApplication對象注冊下面一系列事件:
AcquireRequestState 當(dāng)ASP.NET運(yùn)行時準(zhǔn)備好接收當(dāng)前HTTP請求的對話狀態(tài)的時候引發(fā)這個事件。
AuthenticateRequest 當(dāng)ASP.NET 運(yùn)行時準(zhǔn)備驗證用戶身份的時候引發(fā)這個事件。
AuthorizeRequest 當(dāng)ASP.NET運(yùn)行時準(zhǔn)備授權(quán)用戶訪問資源的時候引發(fā)這個事件。
BeginRequest 當(dāng)ASP.NET運(yùn)行時接收到新的HTTP請求的時候引發(fā)這個事件。
Disposed 當(dāng)ASP.NET完成HTTP請求的處理過程時引發(fā)這個事件。
EndRequest 把響應(yīng)內(nèi)容發(fā)送到客戶端之前引發(fā)這個事件。
Error 在處理HTTP請求的過程中出現(xiàn)未處理異常的時候引發(fā)這個事件。
PostRequestHandlerExecute 在HTTP處理程序結(jié)束執(zhí)行的時候引發(fā)這個事件。
PreRequestHandlerExecute 在ASP.NET開始執(zhí)行HTTP請求的處理程序之前引發(fā)這個事件。在這個事件之后,ASP.NET 把該請求轉(zhuǎn)發(fā)給適當(dāng)?shù)腍TTP處理程序。
PreSendRequestContent 在ASP.NET把響應(yīng)內(nèi)容發(fā)送到客戶端之前引發(fā)這個事件。這個事件允許我們在內(nèi)容到達(dá)客戶端之前改變響應(yīng)內(nèi)容。我們可以使用這個事件給頁面輸出添加用于所有頁面的內(nèi)容。例如通用菜單、頭信息或腳信息。
PreSendRequestHeaders 在ASP.NET把HTTP響應(yīng)頭信息發(fā)送給客戶端之前引發(fā)這個事件。在頭信息到達(dá)客戶端之前,這個事件允許我們改變它的內(nèi)容。我們可以使用這個事件在頭信息中添加cookie和自定義數(shù)據(jù)。
ReleaseRequestState 當(dāng)ASP.NET結(jié)束所搜有的請求處理程序執(zhí)行的時候引發(fā)這個事件。
ResolveRequestCache 我們引發(fā)這個事件來決定是否可以使用從輸出緩沖返回的內(nèi)容來結(jié)束請求。這依賴于Web應(yīng)用程序的輸出緩沖時怎樣設(shè)置的。
UpdateRequestCache 當(dāng)ASP.NET完成了當(dāng)前的HTTP請求的處理,并且輸出內(nèi)容已經(jīng)準(zhǔn)備好添加給輸出緩沖的時候,引發(fā)這個事件。這依賴于Web應(yīng)用程序的輸出緩沖是如何設(shè)置的。
上面這么多的事件,我們看起來可能會有些眼暈,但沒關(guān)系,下面一步一步地看。
HttpModule生命周期示意圖
下面是事件的觸發(fā)順序:
BeginRequest和PreRequestHandlerExecute之間的事件是在服務(wù)器執(zhí)行HttpHandler處理之前觸發(fā)。
PostRequestHandlerExecute和PreSendRequestContent之間的事件是在服務(wù)器執(zhí)行Handler處理之后觸發(fā)。
?
?
下面我們看一下如何使用HttpModule來實現(xiàn)我們?nèi)粘5膽?yīng)用:
HttpModule通過在某些事件中注冊,把自己插入ASP.NET請求處理管道。當(dāng)這些事件發(fā)生的時候,ASP.NET調(diào)用對相應(yīng)的HTTP模塊,這樣該模塊就能處理請求了。
1、向每個頁面動態(tài)添加一些備注或說明性的文字:
有的網(wǎng)站每一個頁面都會彈出一個廣告或在每個頁面都以注釋形式(<!-- -->)加入網(wǎng)站的版權(quán)信息。如果在每個頁面教編寫這樣的JS代碼的話,對于大一點的網(wǎng)站,這種JS代碼的編寫與維護(hù)可是一個很繁瑣枯燥的工作。
有了HttpModule我們就可以很簡單地解決這個問題了。HttpModule是客戶端發(fā)出請求到客戶端接收到服務(wù)器響應(yīng)之間的一段必經(jīng)之 路。我們完全可以在服務(wù)器處理完請求之后,并在向客戶端發(fā)送響應(yīng)文本之前這段時機(jī),把這段注釋文字添加到頁面文本之后。這樣,每一個頁面請求都會被附加上 這段注釋文字。
這段代碼究竟該在哪個事件里實現(xiàn)呢? PostRequestHandlerExecute和PreSendRequestContent之間的任何一個事件都可以,但我比較喜歡在EndRequest事件里編寫代碼。
第一步:創(chuàng)建一個類庫ClassLibrary831。
第二步:編寫一個類實現(xiàn)IHttpModule接口
class TestModule:IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
}
}
第三步:在Init事件中注冊EndRequest事件,并實現(xiàn)事件處理方法
class TestModule:IHttpModule
{
public void Dispose(){}
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(context_EndRequest);
}
void context_EndRequest(object sender, EventArgs e)
{
HttpApplication ha = (HttpApplication)sender;
ha.Response.Write("<!--這是每個頁面都會動態(tài)生成的文字。--grayworm-->");
}
}
第四步:在Web.Conofig中注冊一下這個HttpModule模塊
<httpModules>
<add name="TestModule" type="ClassLibrary831.TestModule,ClassLibrary831"></add>
</httpModules>
name:模塊名稱,一般是類名
type:有兩部分組成,前半部分是命名空間和類名組成的全名,后半部分是程序集名稱,如果類是直接放在App_Code文件夾中,那程序名稱是App_Code。
這樣在Web站點是添加該類庫的引用后,運(yùn)行每個頁面,會發(fā)現(xiàn)其源文件中都會加入“<!--這是每個頁面都會動態(tài)生成的文字。--grayworm-->”這句話。同樣的方法你也可以在其中加入JS代碼。
2、身份檢查
大家在作登錄時,登錄成功后,一般要把用戶名放在Session中保存,在其它每一個頁面的Page_Load事件中都檢查Session中是否存在用戶名,如果不存在就說明用戶未登錄,就不讓其訪問其中的內(nèi)容。
在比較大的程序中,這種做法實在是太笨拙,因為你幾乎要在每一個頁面中都加入檢測Session的代碼,導(dǎo)致難以開發(fā)和維護(hù)。下面我們看看如何使用HttpModule來減少我們的工作量
由于在這里我們要用到Session中的內(nèi)容,我們只能在AcquireRequestState和 PreRequestHandlerExecute事件中編寫代碼,因為在HttpModule中只有這兩事件中可以訪問Session。這里我們選擇 PreRequestHandlerExecute事件編寫代碼。
第一步:創(chuàng)建一個類庫ClassLibrary831。
第二步:編寫一個類實現(xiàn)IHttpModule接口
class TestModule:IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
}
}
第三步:在Init事件中注冊PreRequestHandlerExecute事件,并實現(xiàn)事件處理方法
class AuthenticModule:IHttpModule
{
public void Dispose(){}
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
}
void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication ha = (HttpApplication)sender;
string path = ha.Context.Request.Url.ToString();
int n = path.ToLower().IndexOf("Login.aspx");
if (n == -1) //是否是登錄頁面,不是登錄頁面的話則進(jìn)入{}
{
if (ha.Context.Session["user"] == null) //是否Session中有用戶名,若是空的話,轉(zhuǎn)向登錄頁。
{
ha.Context.Response.Redirect("Login.aspx?source=" + path);
}
}
}
}
?
?
第四步:在Login.aspx頁面的“登錄”按鈕中加入下面代碼
protected void Button1_Click(object sender, EventArgs e)
{
if(true)??? //判斷用戶名密碼是否正確
{
if (Request.QueryString["source"] != null)
{
string s = Request.QueryString["source"].ToLower().ToString();?? //取出從哪個頁面轉(zhuǎn)來的
Session["user"] = txtUID.Text;
Response.Redirect(s); //轉(zhuǎn)到用戶想去的頁面
}
else
{
Response.Redirect("main.aspx");??? //默認(rèn)轉(zhuǎn)向main.aspx
}
}
}
第五步:在Web.Conofig中注冊一下這個HttpModule模塊
<httpModules>
<add name="TestModule" type="ClassLibrary831.TestModule,ClassLibrary831"></add>
</httpModules>
3、多模塊的操作
如果定義了多個HttpModule,在web.config文件中引入自定義HttpModule的順序就決定了多個自定義HttpModule在處理一個HTTP請求的接管順序。
HttpHandler
HttpHandler是HTTP請求的處理中心,真正地對客戶端請求的服務(wù)器頁面做出編譯和執(zhí)行,并將處理過后的信息附加在HTTP請求信息流中再次返回到HttpModule中。
HttpHandler與HttpModule不同,一旦定義了自己的HttpHandler類,那么它對系統(tǒng)的HttpHandler的關(guān)系將是“覆蓋”關(guān)系。
IHttpHandler接口聲明
public interface IHttpHandler
{
bool IsReusable { get; }
public void ProcessRequest(HttpContext context); //請求處理函數(shù)
}
示例:把硬盤上的圖片以流的方式寫在頁面上
class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);
byte[] b = new byte[fs.Length];
fs.Read(b, 0, (int)fs.Length);
fs.Close();
context.Response.OutputStream.Write(b, 0, b.Length);
}
public bool IsReusable
{
get
{
return true;
}
}
}
Web.Config配置文件
<httpHandlers>
<add verb="*" path="*" type="ClassLibrary831.TestHandler,ClassLibrary831"></add>
</httpHandlers>
Verb屬性:指定了處理程序支持的HTTP動作。*-支持所有的HTTP動作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持兩種操作。
Path屬性:指定了需要調(diào)用處理程序的路徑和文件名(可以包含通配符)。“*”、“*.aspx”、“showImage.aspx”、“test1.aspx,test2.aspx”
Type屬性:用名字空間、類名稱和程序集名稱的組合形式指定處理程序或處理程序工廠的實際類型。ASP.NET運(yùn)行時首先搜索bin目錄中的DLL,接著在GAC中搜索。
這樣程序運(yùn)行的效果是該網(wǎng)站的任何一個頁面都會顯示worm.jpg圖片。如何只讓一個頁面(default21.aspx)執(zhí)行 HttpHandler中的ProcessRequest方法呢?最簡單的辦法是在Web.Config文件中把path配置信息設(shè)為 default21.aspx。
?
?
根據(jù)這個例子大家可以考慮一下如何編寫“驗證碼”了。
IHttpHandler工廠
IHttpHandlerFactory的作用是對IHttpHandler進(jìn)行管理。工廠的作用請見http://hi.baidu.com/grayworm/blog/item/4a832160f8c9de46eaf8f8c1.html"
IHttpHandlerFactory接口的聲明:
public interface IHttpHandlerFactory
{
IHttpHandler GetHandler (HttpContext context,string requestType,string url,string pathTranslated);
void ReleaseHandler (IHttpHandler handler);
}
GetHandler返回實現(xiàn)IHttpHandler接口的類的實例,ReleaseHandler使工廠可以重用現(xiàn)有的處理程序?qū)嵗?/p>
示例:兩個用IHttpHandlerFactory來實現(xiàn)對不同HttpHandler的調(diào)用。
有兩個HttpHandler:將圖片顯示在頁面上的HttpHandler和生成驗證碼的Handler
//將圖片顯示在頁面上的Handler
class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);
byte[] b = new byte[fs.Length];
fs.Read(b, 0, (int)fs.Length);
fs.Close();
context.Response.OutputStream.Write(b, 0, b.Length);
}
public bool IsReusable
{
get
{
return true;
}
}
}
//生成驗證碼的Handler
class CodeHandler:IHttpHandler
{
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext context)
{
Image b = new Bitmap(50,20);
Graphics g = Graphics.FromImage(b);
SolidBrush sb = new SolidBrush(Color.White);
Font f = new Font("宋體", 12);
string str = "";
Random r = new Random();
for (int i = 0; i < 4; i++)
{
str += r.Next(10);
}
g.DrawString(str,f,sb,0,0);
b.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
IHttpHandler工廠
class TestHandlerFactory : IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string fname = url.Substring(url.IndexOf('/') + 1);
while (fname.IndexOf('/') != -1)
fname = fname.Substring(fname.IndexOf('/') + 1);
string cname = fname.Substring(0, fname.IndexOf('.'));
string className ="";
className = "ClassLibrary831.CodeHandler";
object h = null;
try
{
//h = new TestHandler();
h = Activator.CreateInstance(Type.GetType(className));
}
catch (Exception e)
{
throw new HttpException("工廠不能為類型" + cname + "創(chuàng)建實例。", e);
}
return (IHttpHandler)h;
}
public void ReleaseHandler(IHttpHandler handler)
{
}
}(車延祿)
配置文件
<httpHandlers>
<add verb="*" path="default21.aspx,default22.aspx" type="ClassLibrary831.TestHandlerFactory,ClassLibrary831"></add>
</httpHandlers>
這樣TestHandlerFactory就會根據(jù)請求的不同頁面執(zhí)行不同的HttpHandler處理程序了。
?
?
?
HttpHandler使用會話
如果要在處理程序中使用Session,那必須把該HttpHandler實現(xiàn)IRequiresSessionState接口,,IRequiresSessionState接口是個空接口,它沒有抽象方法,只是一個標(biāo)記。此處就不作例子驗證了
ASP.Net處理Http Request時,使用Pipeline(管道)方式,由各個HttpModule對請求進(jìn)行處理,然后到達(dá) HttpHandler,HttpHandler處理完之后,仍經(jīng)過Pipeline中各個HttpModule的處理,最后將HTML發(fā)送到客戶端瀏覽 器中。
生命周期中涉及到幾個非常重要的對象:HttpHandler,HttpModule,IHttpHandlerFactory,他們的執(zhí)行(順序)大致的執(zhí)行過程是這樣的:client端發(fā)送頁面請求,被IIS的某個進(jìn)程截獲,它根據(jù)申請的頁 面后綴(.aspx)不同,調(diào)用不同的頁面處理程序(.asp->asp.dll; .aspx->ISAPI.dll).而頁面處理程序在處理過程中,則要經(jīng)歷HttpModule,HttpHandler的處理:前者HttpModule用于頁面處理前和處理后的一些事件的處理,后者HttpHandler進(jìn)行真正的頁面的處理。
如前所說,HttpModule會在頁面處理前和后對頁面進(jìn)行處理,所以它不會影響真正的頁面請求。通常用在給每個頁面的頭部或者尾部添加一些信息(如版 權(quán)聲明)等.曾經(jīng)見過一些免費(fèi)的空間,我們的頁面上傳上去后,瀏覽的時候發(fā)現(xiàn),在每個頁面的頭部和尾部多了很多小廣告....,如果理解了 HttpModule的原理,要做這個就不是很難了~
IHttpModule與IHttpHandler的區(qū)別整理
1.先后次序.先IHttpModule,后IHttpHandler. 注:Module要看你響應(yīng)了哪個事件,一些事件是在Handler之前運(yùn)行的,一些是在Handler之后運(yùn)行的
2.對請求的處理上:
IHttpModule是屬于大小通吃類型,無論客戶端請求的是什么文件,都會調(diào)用到它;例如aspx,rar,html的請求.
IHttpHandler則屬于挑食類型,只有ASP.net注冊過的文件類型(例如aspx,asmx等等)才會輪到調(diào)用它.
3.IHttpHandler按照你的請求 生成響應(yīng)的內(nèi)容,IHttpModule對請求進(jìn)行預(yù)處理,如驗證、修改、過濾等等,同時也可以對響應(yīng)進(jìn)行處理
ASP.Net系統(tǒng)本身配置有很多HttpHandler和HttpModule,以處理aspx等.Net標(biāo)準(zhǔn)的頁面文件,以及這些頁面文件中標(biāo) 準(zhǔn)的事件處理等。查看%System%/Microsoft.NET/Framework/v2.0.50727/CONFIG目錄下的 web.config文件中的httpHandlers和httpModules節(jié)點,可以看到這些配置。如果有興趣,可以使用Reflector查 看.Net系統(tǒng)中相關(guān)的類和方法,了解.Net如何處理以及做了什么處理。
.Net也提供了一套機(jī)制來開發(fā)自定義的HttpHandler和 HttpModule,均可以用于對HttpRequest的截取,完成自定義的處理。 HttpModule 繼承System.Web.IHttpModule接口,實現(xiàn)自己的HttpModule類。必須要實現(xiàn)接口的兩個方法:Init和Dispose。在 Init中,可以添加需要截取的事件;Dispose用于資源的釋放,如果在Init中創(chuàng)建了自己的資源對象,請在Dispose中進(jìn)行釋放。
namespace MyModule
{
public class MyHttpModule : IHttpModule
{
public MyHttpModule()
{
}
//Init方法用來注冊HttpApplication 事件。
public void Init(HttpApplication r_objApplication)
{
r_objApplication.BeginRequest += new EventHandler(this.BeginRequest);
}
public void Dispose()
{
}
private void BeginRequest(object r_objSender, EventArgs r_objEventArgs)
{
HttpApplication objApp = (HttpApplication)r_objSender;
objApp.Response.Write("您請求的URL為" + objApp.Request.Path);
}
}
}
將編譯的dll文件拷貝到web項目的bin目錄下,在web項目的web.config文件system.web節(jié)點中配置:
這樣就將自定義的HttpModule類MyHttpModule插入到了當(dāng)前web的HttpModule的Pipeline中。 HttpModule主要功能是對Application的各個事件進(jìn)行截取,在這些事件中完成自己的處理。其實如果自己開發(fā)一些項目,直接在 Global.asax中處理已經(jīng)足夠了。如果是開發(fā)一個Framework或者是某些方面的組件,需要在事件中添加處理,開發(fā)自定義的 HttpModule,可以避免使用Framework或者組件時,還得手工在Global.asax中添加代碼。???? 目前想到的開發(fā)自定義HttpModule的用途,有全局的身份/權(quán)限驗證、自定義網(wǎng)站訪問/操作日志的記錄、處于管理/調(diào)試等目的對站點進(jìn)行監(jiān)控追蹤 等。當(dāng)然,如果是結(jié)合自定義的HttpHandler進(jìn)行Framework的開發(fā),HttpModule可以用于其它的一些特殊的處理。
<httpModules>
<add name="test" type="MyHttpModuleTest.MyHttpModule,MyHttpModule"/>
</httpModules>
注意要區(qū)分大小寫,因為web.config作為一個XML文件是大小寫敏感的。“type=MyHttpModuleTest.MyHttpModule,MyHttpModule”告訴我們
系統(tǒng)將會將http request請求交給位于MyHttpModule.dll文件中的MyHttpModuleTest.MyHttpModule類去處理。
HttpHandler是完全的對Http Request的截取。
首先,繼承System.Web.IHttpHandler接口,實現(xiàn)自己的HttpHandler類。必須要實現(xiàn)接口的ProcessRequest方 法和IsReusable屬性。ProcessRequest方法中完成對每個Http Request的處理,發(fā)送處理結(jié)果的HTML到輸出緩存中。IsReusable屬性被.Net Framework調(diào)用,用以確定這個HttpHandler的實例是否可以被重用于同類型其它的Request處理。
如果你在自己的HttpHandler類中,需要讀取或者是寫Session值,需要再繼承一個接口IRequiresSessionState。這個接 口沒有任何方法,只是一個標(biāo)記接口。繼承這個接口之后,就可以在自己的HttpHandler中訪問Session,可以在Session中寫入值。
namespace MyHandler
{
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
public MyHttpHandler() {}
public bool IsReusable
{
get { return true; }
}
?
?
?
public void ProcessRequest(HttpContext context)
{
HttpResponse objResponse = context.Response ;
objResponse.Write("
This request is handled by MyHttpHandler
");
}
}
}
把編譯的dll文件拷貝到web項目的bin目錄下。
接下來,這樣來測試一下MyHttpHandler。我們?yōu)镮IS配置一個以.cc為后綴名的文件類型,用我們寫的MyHttpHandler來處理。
首先,在IIS站點的Configuration配置里面,添加一個對.cc后綴名處理的Application Extention Mapping項。
然后,在web項目的web.config節(jié)點節(jié)點中配置:
MyHttpHandler, MyHandler"/>
verb屬性配置這個HttpHandler處理那些HTTP方法,例如GET、POST等,如果是處理所有方法,就用*。path屬性配置HttpHandler對哪些文件進(jìn)行處理,例如可以是myfile.cc,如果是處理所有的.cc文件,就用*.cc。
這樣,這個站點上所有.cc類型文件的訪問,都由MyHttpHandler處理。使用http://localhost/站點虛擬目錄/a.cc訪問測試站點,可以看到測試效果。當(dāng)然,a.cc這個文件在Web服務(wù)器上是并不存在的。
對HttpHandler的使用,比較典型的有.Net的Web MVC開源項目Maverick。Maverick使用一個Dispatcher類對所有的Http Request進(jìn)行截取,他以.m作為后綴名向Web服務(wù)器提交請求,在Dispatcher中,將.m的后綴去掉,提取Command Name,然后以這個command name從配置文件中加載處理的flow,形成一個chain,依次對chain上的各個command和view進(jìn)行處理,對各個command和 view的處理結(jié)果可能會在chain中選擇不同的處理分支,每個處理的Step中將處理結(jié)果的HTML寫入Response的緩存中進(jìn)行輸出。
總體來說,Maverick的框架架構(gòu)概念很不錯,但也存在明顯的缺陷,以后有時間再詳細(xì)的寫寫它的架構(gòu)和需要改進(jìn)之處。
總之,將HttpModule、HttpHandler,以及使用Ajax等將客戶端進(jìn)行封裝結(jié)合起來,能夠給web項目的開發(fā)帶來非常大的改善空間。
Asp.Net HttpHandler實現(xiàn)URL重寫的
我們經(jīng)常看到很多網(wǎng)站訪問文章的時候才用的是***.html 或***.shtml (如本blog的日志訪問效果),其時這寫文件在服務(wù)器上不存在的,那為什么會出現(xiàn)這樣的效果呢,是因為Web服務(wù)器上對URL執(zhí)行了重寫,把訪問的 URL根據(jù)特定的格式重寫成內(nèi)部訪問頁面來實現(xiàn)的,它的好處是便于用戶理解,同時搜索引擎也能更好地收入你的網(wǎng)站,當(dāng)然其它的好處也很多,這里不做一一介 紹了。
本文所講的是使用Asp.Net中的HttpHandler實現(xiàn)URL重寫的,它所實現(xiàn)的原理請看這里,本程序可以處理任何Url,因為我在程序中使用了URL過慮,只有訪問文件名是數(shù)字的才進(jìn)行處理,并指在內(nèi)部執(zhí)行一個新的頁面,并輸出數(shù)據(jù),代碼如下:
public void ProcessRequest(HttpContext Context)
{
try {
//申明Request
HttpRequest Request = Context.Request;
//取來路Url的絕對路徑
string Url = Request.Url.AbsolutePath;
//取訪問的Web文件的開始字符間隔數(shù)
int RegStart = Url.LastIndexOf("/") + 1;
//申明一個確定Web文件名是否全是數(shù)字
Regex Reg = new Regex(@"/d+");
//用正則表達(dá)式進(jìn)行匹配
if (Reg.IsMatch(Url, RegStart))
{
// 如果web文件名是數(shù)字,則判定是查詢相關(guān)文章,執(zhí)行指定頁面???????????? Context.Server.Execute("~/PermaLink.aspx?id=" + Reg.Match(Url, RegStart).Value);
}
}
catch
{
Context.Response.Redirect(Context.Request.Url.ToString());
}
}
當(dāng)然你首先要做的是先建一個類,并繼承自IHttpHandler,然后把這段代碼拷入,并編譯。在Web項目中若要使用此功能,需要在web.config里面加上如下語句:
<httpHandlers>
<add verb="*" path="*.shtml" type="HttpHandle.UrlRewrite" />
</httpHandlers>
同時,還要在IIS中對Web項目進(jìn)行配置,在Web項目的屬性中,在主目錄選項卡里,把執(zhí)行權(quán)限改為"腳本和可執(zhí)行文件",然后打開配置,在應(yīng)用程序擴(kuò)展里加上需重寫的文件格式的擴(kuò)展,好了,成事具備,只欠運(yùn)行了。
轉(zhuǎn)載于:https://www.cnblogs.com/wuyanbin/p/3200553.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的asp.net 页面全生命周期的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 20130717随想
- 下一篇: iOS toolchain based