在之前的ASP.NET是如何在IIS下工作的這篇文章中介紹了ASP.NET與IIS配合工作的機制,在http請求經(jīng)過一系列處理后,最后到達ASP.NET管道中,這時,就是Http Modules和HttpHandler出場的時候了。
再來擺出管道工作時序圖來一看:
HttpModule
HttpModule是類似于過濾器的作用,可以沒有,也可以有任意個,每一個都可以訂閱管道事件中的任意個事件,在每個訂閱的事件中可自定義功能實現(xiàn)。
HttpModule是實現(xiàn)IHttpModule接口的類。接口如下:
public?interface?IHttpModule??????{??????????//?摘要:???????????//?????處置由實現(xiàn)?System.Web.IHttpModule?的模塊使用的資源(內(nèi)存除外)。??????????void?Dispose();??????????//??????????//?摘要:???????????//?????初始化模塊,并使其為處理請求做好準備。??????????//??????????//?參數(shù):???????????//??context:??????????//??一個?System.Web.HttpApplication,它提供對?ASP.NET?應用程序內(nèi)所有應用程序?qū)ο蟮墓玫姆椒?、屬性和事件的訪問??????????void?Init(HttpApplication?context);??????}?? 下面實現(xiàn)一個HttpModule,并訂閱管道中的一系列事件,訂閱事件就是在Init方法中綁定EventHandler的過程:
代碼有點長,因為我把每一個事件都訂閱了,這樣一來可以清楚的看出哪些事件執(zhí)行了,這些事件執(zhí)行的先后順序是什么。代碼如下:
public?class?MyModule?:?IHttpModule??????{??????????#region?IHttpModule?Members????????????public?void?Dispose()??????????{????????????????????????}????????????public?void?Init(HttpApplication?context)??????????{??????????????????????????????????????????context.LogRequest?+=?new?EventHandler(OnLogRequest);??????????????context.BeginRequest?+=?new?EventHandler(context_BeginRequest);??????????????context.AuthenticateRequest?+=?new?EventHandler(context_AuthenticateRequest);??????????????context.AcquireRequestState?+=?new?EventHandler(context_AcquireRequestState);??????????????context.AuthorizeRequest?+=?new?EventHandler(context_AuthorizeRequest);??????????????context.Disposed?+=?new?EventHandler(context_Disposed);??????????????context.Error?+=?new?EventHandler(context_Error);??????????????context.EndRequest?+=?new?EventHandler(context_EndRequest);??????????????context.MapRequestHandler?+=?new?EventHandler(context_MapRequestHandler);??????????????context.PostAcquireRequestState?+=?new?EventHandler(context_PostAcquireRequestState);??????????????context.PostAuthenticateRequest?+=?new?EventHandler(context_PostAuthenticateRequest);??????????????context.PostAuthorizeRequest?+=?new?EventHandler(context_PostAuthorizeRequest);??????????????context.PostLogRequest?+=?new?EventHandler(context_PostLogRequest);??????????????context.PostReleaseRequestState?+=?new?EventHandler(context_PostReleaseRequestState);??????????????context.PostRequestHandlerExecute?+=?new?EventHandler(context_PostRequestHandlerExecute);??????????????context.PostResolveRequestCache?+=?new?EventHandler(context_PostResolveRequestCache);??????????????context.PostUpdateRequestCache?+=?new?EventHandler(context_PostUpdateRequestCache);??????????????context.ReleaseRequestState?+=?new?EventHandler(context_ReleaseRequestState);??????????????context.RequestCompleted?+=?new?EventHandler(context_RequestCompleted);??????????????context.ResolveRequestCache?+=?new?EventHandler(context_ResolveRequestCache);??????????????context.UpdateRequestCache?+=?new?EventHandler(context_UpdateRequestCache);??????????????context.PreRequestHandlerExecute?+=?new?EventHandler(context_PreRequestHandlerExecute);??????????????context.PreSendRequestContent?+=?new?EventHandler(context_PreSendRequestContent);??????????????context.PreSendRequestHeaders?+=?new?EventHandler(context_PreSendRequestHeaders);??????????????context.PostMapRequestHandler?+=?new?EventHandler(context_PostMapRequestHandler);??????????????}????????????void?context_Error(object?sender,?EventArgs?e)??????????{??????????????WriteLog("Error");????????????????????????}????????????void?context_UpdateRequestCache(object?sender,?EventArgs?e)??????????{??????????????WriteLog("UpdateRequestCache");????????????????????????}????????????void?context_ResolveRequestCache(object?sender,?EventArgs?e)??????????{??????????????WriteLog("ResolveRequestCache");????????????????????????}????????????void?context_RequestCompleted(object?sender,?EventArgs?e)??????????{??????????????WriteLog("RequestCompleted");????????????????????????}????????????void?context_ReleaseRequestState(object?sender,?EventArgs?e)??????????{??????????????WriteLog("ReleaseRequestState");????????????????????????}????????????void?context_PostUpdateRequestCache(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostUpdateRequestCache");????????????????????????}????????????void?context_PostResolveRequestCache(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostResolveRequestCache");????????????????????????}????????????void?context_PostRequestHandlerExecute(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostRequestHandlerExecute");????????????????????????}????????????void?context_PostReleaseRequestState(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostReleaseRequestState");????????????????????????}????????????void?context_PostLogRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostLogRequest");????????????????????????}????????????void?context_PostAuthorizeRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostAuthorizeRequest");????????????????????????}????????????void?context_PostAuthenticateRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostAuthenticateRequest");????????????????????????}????????????void?context_PostAcquireRequestState(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostAcquireRequestState");????????????????????????}????????????void?context_MapRequestHandler(object?sender,?EventArgs?e)??????????{??????????????WriteLog("MapRequestHandler");????????????????????????}????????????void?context_Disposed(object?sender,?EventArgs?e)??????????{??????????????WriteLog("Disposed");????????????????????????}????????????void?context_AuthorizeRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("AuthorizeRequest");????????????????????????}????????????void?context_AcquireRequestState(object?sender,?EventArgs?e)??????????{??????????????WriteLog("AcquireRequestState");????????????????????????}??????????????void?context_PreSendRequestHeaders(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PreSendRequestHeaders");????????????????????????}????????????void?context_PreSendRequestContent(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PreSendRequestContent");????????????????????????}????????????void?context_PreRequestHandlerExecute(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PreRequestHandlerExecute");????????????????????????}????????????void?context_EndRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("EndRequest");????????????????????????}????????????void?context_BeginRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("*******************************************************************************");??????????????HttpApplication?app?=?sender?as?HttpApplication;??????????????WriteLog(app.Context.Request.Path);??????????????WriteLog("BeginRequest");????????????????????????}????????????void?context_AuthenticateRequest(object?sender,?EventArgs?e)??????????{??????????????WriteLog("AuthenticateRequest");????????????????????????}??????????#endregion????????????public?void?OnLogRequest(Object?source,?EventArgs?e)??????????{????????????????????????????WriteLog("OnLogRequest");????????????????????????}????????????public?void?context_PostMapRequestHandler(object?sender,?EventArgs?e)??????????{??????????????WriteLog("PostMapRequestHandler");????????????????????????}????????????public?void?WriteLog(string?message)??????????{??????????????string?path?=?@"d:\aspnet\httpmodule.txt";??????????????StreamWriter?writer?=?null;??????????????if?(!File.Exists(path))??????????????{??????????????????writer?=?File.CreateText(path);??????????????}??????????????else??????????????{??????????????????FileInfo?info?=?new?FileInfo(path);??????????????????writer?=?info.AppendText();????????????????}??????????????writer.WriteLine(message);????????????????writer.Flush();??????????????writer.Close();??????????}??????}?? 訂閱的事件實現(xiàn)中,將事件名稱保存到我本地D盤的一個文本文件中。
代碼實現(xiàn)完畢了,下一步就是要代碼起作用了,很簡單,只需要在web.config中簡單配置就可以了。配置中注意IIS7集成模式和IIS7經(jīng)典模式(包括IIS6)的區(qū)別,配置如下:
??<system.web>??????<httpModules>????????<add?name="mycustommodule"?type="fengzheng.MyModule,handler_modules"/>??????</httpModules>????</system.web>????<system.webServer>??????<modules>????????<add?name="mycustommodule"?type="fengzheng.MyModule,handler_modules"/>??????</modules>??</system.webServer>?? 如此一來,一個HttpModule及其配置工作就完成了,接下來,發(fā)布網(wǎng)站到IIS或者直接在VS中運行,隨便訪問項目中的一個文件(任何文件類型都可以),我的項目中有一個WebForm2.aspx的頁面,我在瀏覽器中訪問這個頁面,發(fā)現(xiàn)頁面是空白的,因為頁面中我什么都沒寫,上面的Module實現(xiàn)中,我把輸出全部放到本地D盤的一個文本文件中了,ok,打開那個文本文件。如圖:
我們看到輸出內(nèi)容,第2行是訪問的頁面地址,下面依次為訂閱的事件輸出,我們清楚的看到了事件的執(zhí)行順序。
BeginRequest????????????????#發(fā)出信號表示創(chuàng)建任何給定的新請求。?此事件始終被引發(fā),并且始終是請求處理期間發(fā)生的第一個事件??AuthenticateRequest?????????#發(fā)出信號表示配置的身份驗證機制已對當前請求進行了身份驗證。?訂閱?AuthenticateRequest?事件可確保在處理附加模塊或事件處理程序之前對請求進行身份驗證??PostAuthenticateRequest?????#預訂?PostAuthenticateRequest?事件的功能可以訪問由?PostAuthenticateRequest?處理的任何數(shù)據(jù)??AuthorizeRequest????????????#發(fā)出信號表示?ASP.NET?已對當前請求進行了授權。?訂閱?AuthorizeRequest?事件可確保在處理附加的模塊或事件處理程序之前對請求進行身份驗證和授權??PostAuthorizeRequest????????#發(fā)出信號表示?ASP.NET?已對當前請求進行了授權。?訂閱?PostAuthorizeRequest?事件可確保在處理附加的模塊或處理程序之前對請求進行身份驗證和授權??ResolveRequestCache?????????#引發(fā)這個事件來決定是否可以使用從輸出緩沖返回的內(nèi)容來結束請求。這依賴于Web應用程序的輸出緩沖時怎樣設置的??PostResolveRequestCache?????#在?ASP.NET?跳過當前事件處理程序的執(zhí)行并允許緩存模塊滿足來自緩存的請求時發(fā)生??MapRequestHandler???????????#ASP.NET?基礎結構使用?MapRequestHandler?事件來確定用于當前請求的請求處理程序??PostMapRequestHandler???????#在?ASP.NET?已將當前請求映射到相應的事件處理程序時發(fā)生??AcquireRequestState?????????#當?ASP.NET?獲取與當前請求關聯(lián)的當前狀態(tài)(如會話狀態(tài))時發(fā)生??PostAcquireRequestState?????#預訂?AcquireRequestState?事件的功能可以訪問由?PostAcquireRequestState?處理的任何數(shù)據(jù)??PreRequestHandlerExecute????#在ASP.NET開始執(zhí)行HTTP請求的處理程序之前引發(fā)這個事件。在這個事件之后,ASP.NET?把該請求轉(zhuǎn)發(fā)給適當?shù)腍TTP處理程序??PostRequestHandlerExecute???#在?ASP.NET?事件處理程序(例如,某頁或某個?XML?Web?service)執(zhí)行完畢時發(fā)生??ReleaseRequestState?????????#在?ASP.NET?執(zhí)行完所有請求事件處理程序后發(fā)生。?該事件將使狀態(tài)模塊保存當前狀態(tài)數(shù)據(jù)??PostReleaseRequestState?????#在?ASP.NET?已完成所有請求事件處理程序的執(zhí)行并且請求狀態(tài)數(shù)據(jù)已存儲時發(fā)生??UpdateRequestCache??????????#當?ASP.NET?執(zhí)行完事件處理程序以使緩存模塊存儲將用于從緩存為后續(xù)請求提供服務的響應時發(fā)生??PostUpdateRequestCache??????#在?ASP.NET?完成緩存模塊的更新并存儲了用于從緩存中為后續(xù)請求提供服務的響應后,發(fā)生此事件??OnLogRequest????????????????#恰好在?ASP.NET?為當前請求執(zhí)行任何記錄之前發(fā)生,即使發(fā)生錯誤,也會引發(fā)?LogRequest?事件??PostLogRequest??????????????#在?ASP.NET?處理完?LogRequest?事件的所有事件處理程序后發(fā)生??EndRequest??????????????????#在?ASP.NET?響應請求時作為?HTTP?執(zhí)行管線鏈中的最后一個事件發(fā)生??PreSendRequestContent???????#恰好在?ASP.NET?向客戶端發(fā)送內(nèi)容之前發(fā)生,可能發(fā)生多次??PreSendRequestHeaders???????#恰好在?ASP.NET?向客戶端發(fā)送?HTTP?標頭之前發(fā)生??RequestCompleted????????????#在任何托管模塊和處理程序執(zhí)行后,它使模塊清理資源?? 訪問一個頁面的過程中,依次觸發(fā)了23個事件,而HttpModule可訂閱的事件個數(shù)為25個,觀察發(fā)現(xiàn),Error和Disposed這兩個事件沒有觸發(fā)。Error事件在發(fā)生錯誤的情況下執(zhí)行,而Disposed事件,當我們關閉剛才打開的頁面,再到文本文件里查看,發(fā)現(xiàn)Disposed事件出現(xiàn)了,所以Disposed在會話結束后觸發(fā)。
由于HttpModule的個數(shù)可以有多個,我們可以按照上面的方式定義HttpModule實現(xiàn)類,然后再web.config中增加配置項,就可以實現(xiàn)多個HttpModule同時訂閱管道事件了。
介紹完HttpModule,那么HttpHandler又是什么呢,它又在什么什么時候執(zhí)行呢?接下來看一下HttpHandler。
HttpHandler
HttpHandler是HTTP請求的處理中心,真正地對客戶端請求的服務器頁面做出編譯和執(zhí)行,并將處理過后的信息附加在HTTP請求信息流中再次返回到HttpModule中。??
HttpHandler與HttpModule不同,一旦定義了自己的HttpHandler類,那么它對系統(tǒng)的HttpHandler的關系將是“覆蓋”關系。
HttpHandler是實IHttpHandler接口的類,IHttpHandler接口定義如下:
public?interface?IHttpHandler??{????????????????????????????????????bool?IsReusable?{?get;?}??????????????????????????????????????????????????void?ProcessRequest(HttpContext?context);??}?? 接口中只有一個屬性和一個方法,所以實現(xiàn)一個HttpHandler也很簡單,下面實現(xiàn)一個簡單的HttpHandler,代碼如下:
public?class?MyIISHandler?:?IHttpHandler??????{??????????///?<summary>??????????///?您將需要在網(wǎng)站的?Web.config?文件中配置此處理程序???????????///?并向?IIS?注冊它,然后才能使用它。有關詳細信息,??????????///?請參見下面的鏈接:?http://go.microsoft.com/?linkid=8101007??????????///?</summary>??????????????????????public?bool?IsReusable??????????{??????????????//?如果無法為其他請求重用托管處理程序,則返回?false。??????????????//?如果按請求保留某些狀態(tài)信息,則通常這將為?false。??????????????get?{?return?true;?}??????????}????????????public?void?ProcessRequest(HttpContext?context)??????????{??????????????//在此處寫入您的處理程序?qū)崿F(xiàn)。??????????????WriteLog("請求一個asox頁面");??????????}??????????????????}?? 上面我實現(xiàn)了一個很簡單的HttpHandler類,在ProcessRequest方法中,調(diào)用上面的HttpModule類中寫文本文件的方法,在文本文件中寫入“請求一個asox頁面”,沒錯,是一個asox頁面,我自己定義的文件格式,下面我會在web.config中添加配置項:
????<system.web>??????<httpHandlers>????????<add?name="mycustomhandler"?path="*.asox"?verb="*"?type="fengzheng.MyIISHandler,handler_modules"/>??????</httpHandlers>????</system.web>?????????<system.webServer>??????<handlers>?????????<add?name="mycustomhandler"?path="*.asox"?verb="*"?type="fengzheng.MyIISHandler,handler_modules"/>??????</handlers>????</system.webServer>?? 配置項屬性的解釋:
path:指定了需要調(diào)用處理程序的路徑和文件名(可以包含通配符)?!?”、“*.aspx”、“booklist.aspx”、“test1.aspx,test2.aspx”、“*.asox”、“*.txt”。
verb:指定了處理程序支持的HTTP動作。*-支持所有的HTTP動作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持兩種操作。
type:用名字空間、類名稱和程序集名稱的組合形式指定處理程序或處理程序工廠的實際類型。ASP.NET運行時首先搜索bin目錄中的DLL,接著在GAC中搜索。
接著,發(fā)布站點到IIS。打開IIS,找到當前站點的“處理程序映射”,會發(fā)現(xiàn)多了剛剛配置的HttpHandler,如圖:
沒錯,關于對*.asox這種類型的文件,就可以映射到上面創(chuàng)建的HttpHandler來進行處理,觀察其它條目發(fā)現(xiàn),像*.aspx、*.ashx的處理程序是System.Web.UI.PageHandlerFactory和System.Web.UI.SimpleHandlerFactory這樣的工廠類型。沒錯,可以指定處理程序為一個HttpHandler,也可以指定為一個抽象工廠類型。先不說工廠類型的事兒,訪問一下網(wǎng)站中的asox頁面,看一下文本文件的記錄情況。
起作用了,在HttpModule輸出的一堆信息中,夾雜著HttpHandler的輸出,當然這僅限于訪問asox類型的頁面,因為我只對路徑為*.asox的文件格式做了設置,修改下配置文件,例如將path=”*.asox”改為path=”*.aspx”,那么ASP.NET對*.aspx頁面原有的解析機制將被我們設置的處理程序所覆蓋。
回到上面的輸出內(nèi)容,我們觀察HttpHandler輸出內(nèi)容所在的位置,位于PreRequestHandlerExecute和PostRequestHandlerExecute這兩個事件之間,這與HttpApplication類中的管道事件的創(chuàng)建過程有關。
前面說到了,處理處理程序可以指定為一個工廠類型,下面,我就創(chuàng)建一個工廠類型的處理程序。
這里所說的工廠類型的處理程序,就是實現(xiàn)了IHttpHandlerFactory接口的類,IHttpHandlerFactory接口定義如下:
public?interface?IHttpHandlerFactory??????{????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????IHttpHandler?GetHandler(HttpContext?context,?string?requestType,?string?url,?string?pathTranslated);??????????????????????????????????????????????????????????????????????????????void?ReleaseHandler(IHttpHandler?handler);??????}?? 同樣很簡單,也是只有兩個接口方法,下面是實現(xiàn)這個接口的工廠,代碼如下:
public?class?MyHttpHandlerFactory:IHttpHandlerFactory??{??????????public?IHttpHandler?GetHandler(HttpContext?context,?string?requestType,?string?url,?string?pathTranslated)??????????{????????????????????????????WriteLog(string.Format("requestType:{0}|url:{1}|pathTranslated:{2}",?requestType,?url,?pathTranslated));??????????????????????????????????????????Type?t?=?typeof(MyIISHandler);??????????????IHttpHandler?handler?=??Activator.CreateInstance(t)?as?IHttpHandler;??????????????return?handler;??????????}????????????public?void?ReleaseHandler(IHttpHandler?handler)??????????{??????????}??}?? 方法中,返回了前面創(chuàng)建的那個HttpHander類,依然調(diào)用記錄文本文件的方法輸出內(nèi)容,方便觀察執(zhí)行的實際和具體內(nèi)容。配置文件改改為這個工廠類。
<add?name="mycustomFactoryHandler"?path="*.asox"?verb="*"?type="fengzheng.MyHttpHandlerFactory,handler_modules"/>?? 配置完畢后,訪問網(wǎng)站中的asox頁面,打開文本文件,內(nèi)容如下:
我們發(fā)現(xiàn),工廠類中構造IHttpHandler接口的方法發(fā)生在HttpModule的MapRequestHandler之后,這同樣與HttpApplication類中構造管道事件有關。
說了這么多,那么,HttpModule和HttpHandler究竟能干什么呢?
HttpModule很常用的一個作用就是Url重寫,URLRewriter就是基于HttpModule實現(xiàn)的。
另外,有通過HttpHandler對圖片加水印,防止盜鏈的。
具體的可以參考這篇文章
部署網(wǎng)站注意事項:
網(wǎng)站采用.net 4.0集成模式部署,集成模式是一種統(tǒng)一的請求處理管道,它將ASP.NET請求管道與IIS核心管道組合在一起,這種模式能夠提供更好的性能,能夠?qū)崿F(xiàn)配置和治理的模塊化,而且增加了使用托管代碼模塊擴展IIS時的靈活性。IIS經(jīng)典模式與集成模式的區(qū)別
集成模式和經(jīng)典模式的配置文件稍有不同,部署時需要注意針對不同的部署模式,修改配置文件。在vs2013中新建的web應用程序,默認的web.config內(nèi)容如下:
<?xml?version="1.0"?encoding="utf-8"?>??<!--????有關如何配置?ASP.NET?應用程序的詳細信息,請訪問????http:????-->??<configuration>????<system.web>??????<compilation?debug="true"?targetFramework="4.5"?/>??????<httpRuntime?targetFramework="4.5"?/>????</system.web>??</configuration>?? <?xml?version="1.0"?encoding="utf-8"?>??<!--????有關如何配置?ASP.NET?應用程序的詳細信息,請訪問????http://go.microsoft.com/fwlink/?LinkId=169433????-->??<configuration>????<system.web>??????<compilation?debug="true"?targetFramework="4.5"?/>??????<httpRuntime?targetFramework="4.5"?/>??????<httpModules>????????<add?name="mycustommodule"?type="fengzheng.MyModule,handler_modules"/>??????</httpModules>??????<httpHandlers>????????<add?name="mycustomhandler"?path="*.asox"?verb="*"?type="fengzheng.MyIISHandler,handler_modules"/>??????</httpHandlers>????</system.web>??</configuration>?? 經(jīng)典模式經(jīng)測試總是出現(xiàn)如下錯誤,500.21 - 模塊無法識別:
HTTP?錯誤?500.21?-?Internal?Server?Error??處理程序“PageHandlerFactory-ISAPI-4.0_64bit”在其模塊列表中有一個錯誤模塊“IsapiModule”?? 至于錯誤原因:目前還不是很清楚。
- 按照集成模式部署,配置文件應該如下,將配置信息放到system.webServer節(jié)點之下:
<?xml?version="1.0"?encoding="utf-8"?>??<!--????有關如何配置?ASP.NET?應用程序的詳細信息,請訪問????http://go.microsoft.com/fwlink/?LinkId=169433????-->??<configuration>????<system.web>??????<compilation?debug="true"?targetFramework="4.5"?/>??????<httpRuntime?targetFramework="4.5"?/>????</system.web>????<system.webServer>??????<modules>????????<add?name="mycustommodule"?type="fengzheng.MyModule,handler_modules"/>??????</modules>??????<handlers>????????<add?name="mycustomhandler"?path="*"?verb="*"?type="fengzheng.MyIISHandler,handler_modules"/>??????</handlers>????</system.webServer>??</configuration>??
總結
以上是生活随笔為你收集整理的ASP.NET-自定义HttpModule与HttpHandler的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。