熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

HttpModule與HttpHandler詳解

2013-11-13 10:00:23  來源: .NET編程 

  ASPNET對請求處理的過程

  當請求一個*aspx文件的時候這個請求會被inetinfoexe進程截獲它判斷文件的後綴(aspx)之後將這個請求轉交給 ASPNET_ISAPIdllASPNET_ISAPIdll會通過http管道(Http PipeLine)將請求發送給ASPNET_WPexe進程在ASPNET_WPexe進程中通過HttpRuntime來處理這個請求處理完畢將結果返回客戶端

  inetinfoexe進程是www服務的進程IIS服務和ASPNET_ISAPIDLL都寄存在此進程中

  ASPNET_ISAPIDLL是處理aspx文件的win組件其實IIS服務器是只能識l文件的當IIS服務器發現被請求的文件是aspx文件時IIS服務器將其交給aspnet_isapidll來處理

  aspnet_wpexe進程ASPNET框架進程提運行的托管環境的CLR(公共語言運行時)就是寄存在此進程中

  ASPNET Framework處理一個Http Request的流程

  HttpRequest>inetinfoexe>ASPNET_ISAPIdll>ASPNET_WPexe>HttpRuntime>HttpApplication Factory>HttpApplication>HttpModule>HttpHandler Factory>HttpHandler>HttpHandlerProcessRequest()

  ASPNET請求處理過程是基於管道模型的這個管道模型是由多個HttpModule和HttpHandler組成ASPNET 把http請求依次傳遞給管道中各個HttpModule最終被HttpHandler處理處理完成後再次經過管道中的HTTP模塊把結果返回給客戶端我們可以在每個HttpModule中都可以干預請求的處理過程

  注意在http請求的處理過程中只能調用一個HttpHandler但可以調用多個HttpModule

  當請求到達HttpModule的時候系統還沒有對這個請求真正處理但是我們可以在這個請求傳遞到處理中心(HttpHandler)之前附加一些其它信息或者截獲的這個請求並作一些額外的工作也或者終止請求等在HttpHandler處理完請求之後我們可以再在相應的HttpModule中把請求處理的結果進行再次加工返回客戶端

  HttpModule

  HTTP模塊是實現了SystemWebIhttpModule接口的類

  IHttpModule接口的聲明

  public interface IHttpModule

  {

  void Init (HttpApplication context);

  void Dispose ();

  }

  Init 方法系統初始化的時候自動調用這個方法允許HTTP模塊向HttpApplication 對象中的事件注冊自己的事件處理程序

  Dispose方法 這個方法給予HTTP模塊在對象被垃圾收集之前執行清理的機會此方法一般無需編寫代碼

  HTTP模塊可以向SystemWebHttpApplication對象注冊下面一系列事件

  AcquireRequestState 當ASPNET運行時准備好接收當前HTTP請求的對話狀態的時候引發這個事件

  AuthenticateRequest 當ASPNET 運行時准備驗證用戶身份的時候引發這個事件

  AuthorizeRequest 當ASPNET運行時准備授權用戶訪問資源的時候引發這個事件

  BeginRequest 當ASPNET運行時接收到新的HTTP請求的時候引發這個事件

  Disposed 當ASPNET完成HTTP請求的處理過程時引發這個事件

  EndRequest 把響應內容發送到客戶端之前引發這個事件

  Error 在處理HTTP請求的過程中出現未處理異常的時候引發這個事件

  PostRequestHandlerExecute 在HTTP處理程序結束執行的時候引發這個事件

  PreRequestHandlerExecute 在ASPNET開始執行HTTP請求的處理程序之前引發這個事件在這個事件之後ASPNET 把該請求轉發給適當的HTTP處理程序

  PreSendRequestContent 在ASPNET把響應內容發送到客戶端之前引發這個事件這個事件允許我們在內容到達客戶端之前改變響應內容我們可以使用這個事件給頁面輸出添加用於所有頁面的內容例如通用菜單頭信息或腳信息

  PreSendRequestHeaders 在ASPNET把HTTP響應頭信息發送給客戶端之前引發這個事件在頭信息到達客戶端之前這個事件允許我們改變它的內容我們可以使用這個事件在頭信息中添加cookie和自定義數據

  ReleaseRequestState 當ASPNET結束所搜有的請求處理程序執行的時候引發這個事件

  ResolveRequestCache 我們引發這個事件來決定是否可以使用從輸出緩沖返回的內容來結束請求這依賴於Web應用程序的輸出緩沖時怎樣設置的

  UpdateRequestCache 當ASPNET完成了當前的HTTP請求的處理並且輸出內容已經准備好添加給輸出緩沖的時候引發這個事件這依賴於Web應用程序的輸出緩沖是如何設置的

  BeginRequest和PreRequestHandlerExecute之間的事件是在服務器執行HttpHandler處理之前觸發

  PostRequestHandlerExecute和PreSendRequestContent之間的事件是在服務器執行Handler處理之後觸發

  下面我們看一下如何使用HttpModule來實現我們日常的應用

  HttpModule通過在某些事件中注冊把自己插入ASPNET請求處理管道當這些事件發生的時候ASPNET調用對相應的HTTP模塊這樣該模塊就能處理請求了

  向每個頁面動態添加一些備注或說明性的文字

  有的網站每一個頁面都會彈出一個廣告或在每個頁面都以注釋形式(<! >)加入網站的版權信息如果在每個頁面教編寫這樣的JS代碼的話對於大一點的網站這種JS代碼的編寫與維護可是一個很繁瑣枯燥的工作

  有了HttpModule我們就可以很簡單地解決這個問題了HttpModule是客戶端發出請求到客戶端接收到服務器響應之間的一段必經之路我們完全可以在服務器處理完請求之後並在向客戶端發送響應文本之前這段時機把這段注釋文字添加到頁面文本之後這樣每一個頁面請求都會被附加上這段注釋文字

  這段代碼究竟該在哪個事件裡實現呢? PostRequestHandlerExecute和PreSendRequestContent之間的任何一個事件都可以但我比較喜歡在EndRequest事件裡編寫代碼

  第一步創建一個類庫ClassLibrary

  第二步編寫一個類實現IHttpModule接口

  class TestModule:IHttpModule

  {

  public void Dispose()

  {

  }

  public void Init(HttpApplication context)

  {

  }

  }

  第三步在Init事件中注冊EndRequest事件並實現事件處理方法

  class TestModule:IHttpModule

  {

  public void Dispose(){}

  public void Init(HttpApplication context)

  {

  contextEndRequest += new EventHandler(context_EndRequest);

  }

  void context_EndRequest(object sender EventArgs e)

  {

  HttpApplication ha = (HttpApplication)sender;

  haResponseWrite(<!這是每個頁面都會動態生成的文字grayworm>);

  }

  }

  第四步在WebConofig中注冊一下這個HttpModule模塊

  <httpModules>

  <add name=TestModule type=ClassLibraryTestModuleClassLibrary></add>

  </httpModules>

  name模塊名稱一般是類名

  type有兩部分組成前半部分是命名空間和類名組成的全名後半部分是程序集名稱如果類是直接放在App_Code文件夾中那程序名稱是App_Code

  這樣在Web站點是添加該類庫的引用後運行每個頁面會發現其源文件中都會加入<!這是每個頁面都會動態生成的文字grayworm>這句話同樣的方法你也可以在其中加入JS代碼

  身份檢查

  大家在作登錄時登錄成功後一般要把用戶名放在Session中保存在其它每一個頁面的Page_Load事件中都檢查Session中是否存在用戶名如果不存在就說明用戶未登錄就不讓其訪問其中的內容

  在比較大的程序中這種做法實在是太笨拙因為你幾乎要在每一個頁面中都加入檢測Session的代碼導致難以開發和維護下面我們看看如何使用HttpModule來減少我們的工作量

  由於在這裡我們要用到Session中的內容我們只能在AcquireRequestState和PreRequestHandlerExecute事件中編寫代碼因為在HttpModule中只有這兩事件中可以訪問Session這裡我們選擇PreRequestHandlerExecute事件編寫代碼

  第一步創建一個類庫ClassLibrary

  第二步編寫一個類實現IHttpModule接口

  class TestModule:IHttpModule

  {

  public void Dispose()

  {

  }

  public void Init(HttpApplication context)

  {

  }

  }

  第三步在Init事件中注冊PreRequestHandlerExecute事件並實現事件處理方法

  class AuthenticModule:IHttpModule

  {

  public void Dispose(){}

  public void Init(HttpApplication context)

  {

  contextPreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);

  }

  void context_PreRequestHandlerExecute(object sender EventArgs e)

  {

  HttpApplication ha = (HttpApplication)sender;

  string path = haContextRequestUrlToString();

  int n = pathToLower()IndexOf(Loginaspx);

  if (n == ) //是否是登錄頁面不是登錄頁面的話則進入{}

  {

  if (haContextSession[user] == null) //是否Session中有用戶名若是空的話轉向登錄頁

  {

  haContextResponseRedirect(Loginaspx?source= + path);

  }

  }

  }

  }

  第四步在Loginaspx頁面的登錄按鈕中加入下面代碼

  protected void Button_Click(object sender EventArgs e)

  {

  if(true)    //判斷用戶名密碼是否正確

  {

  if (RequestQueryString[source] != null)

  {

  string s = RequestQueryString[source]ToLower()ToString();   //取出從哪個頁面轉來的

  Session[user] = txtUIDText;

  ResponseRedirect(s); //轉到用戶想去的頁面

  }

  else

  {

  ResponseRedirect(mainaspx);    //默認轉向mainaspx

  }

  }

  }

  第五步在WebConofig中注冊一下這個HttpModule模塊

  <httpModules>

  <add name=TestModule type=ClassLibraryTestModuleClassLibrary></add>

  </httpModules>

  多模塊的操作

  如果定義了多個HttpModule在nfig文件中引入自定義HttpModule的順序就決定了多個自定義HttpModule在處理一個HTTP請求的接管順序

  HttpHandler

  HttpHandler是HTTP請求的處理中心真正地對客戶端請求的服務器頁面做出編譯和執行並將處理過後的信息附加在HTTP請求信息流中再次返回到HttpModule中

  HttpHandler與HttpModule不同一旦定義了自己的HttpHandler類那麼它對系統的HttpHandler的關系將是覆蓋關系

  IHttpHandler接口聲明

  public interface IHttpHandler

  {

  bool IsReusable { get; }

  public void ProcessRequest(HttpContext context); //請求處理函數

  }

  示例把硬盤上的圖片以流的方式寫在頁面上

  class TestHandler : IHttpHandler

  {

  public void ProcessRequest(HttpContext context)

  {

  FileStream fs = new FileStream(contextServerMapPath(wormjpg) FileModeOpen);

  byte[] b = new byte[fsLength];

  fsRead(b (int)fsLength);

  fsClose();

  contextResponseOutputStreamWrite(b bLength);

  }

  public bool IsReusable

  {

  get

  {

  return true;

  }

  }

  }

  WebConfig配置文件

  <httpHandlers>

  <add verb=* path=* type=ClassLibraryTestHandlerClassLibrary></add>

  </httpHandlers>

  Verb屬性指定了處理程序支持的HTTP動作*-支持所有的HTTP動作;GET-支持Get操作;POST-支持Post操作;GET POST-支持兩種操作

  Path屬性指定了需要調用處理程序的路徑和文件名(可以包含通配符)**aspxshowImageaspxtestaspxtestaspx

  Type屬性用名字空間類名稱和程序集名稱的組合形式指定處理程序或處理程序工廠的實際類型ASPNET運行時首先搜索bin目錄中的DLL接著在GAC中搜索

  這樣程序運行的效果是該網站的任何一個頁面都會顯示wormjpg圖片如何只讓一個頁面(defaultaspx)執行HttpHandler 中的ProcessRequest方法呢?最簡單的辦法是在WebConfig文件中把path配置信息設為defaultaspx

  根據這個例子大家可以考慮一下如何編寫驗證碼

  IHttpHandler工廠

  IHttpHandlerFactory的作用是對IHttpHandler進行管理工廠的作用請見ml

  IHttpHandlerFactory接口的聲明

  public interface IHttpHandlerFactory

  {

  IHttpHandler GetHandler (HttpContext contextstring requestTypestring urlstring pathTranslated);

  void ReleaseHandler (IHttpHandler handler);

  }

  GetHandler返回實現IHttpHandler接口的類的實例ReleaseHandler使工廠可以重用現有的處理程序實例

  示例兩個用IHttpHandlerFactory來實現對不同HttpHandler的調用

  有兩個HttpHandler將圖片顯示在頁面上的HttpHandler和生成驗證碼的Handler

  //將圖片顯示在頁面上的Handler

  class TestHandler : IHttpHandler

  {

  public void ProcessRequest(HttpContext context)

  {

  FileStream fs = new FileStream(contextServerMapPath(wormjpg) FileModeOpen);

  byte[] b = new byte[fsLength];

  fsRead(b (int)fsLength);

  fsClose();

  contextResponseOutputStreamWrite(b bLength);

  }

  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();

  Graphics g = GraphicsFromImage(b);

  SolidBrush sb = new SolidBrush(ColorWhite);

  Font f = new Font(宋體 );

  string str = ;

  Random r = new Random();

  for (int i = ; i < ; i++)

  {

  str += rNext();

  }

  gDrawString(strfsb);

  bSave(contextResponseOutputStream SystemDrawingImagingImageFormatJpeg);

  }

  }

  IHttpHandler工廠

  class TestHandlerFactory : IHttpHandlerFactory

  {

  public IHttpHandler GetHandler(HttpContext context string requestType string url string pathTranslated)

  {

  string fname = urlSubstring(urlIndexOf(/) + );

  while (fnameIndexOf(/) != )

  fname = fnameSubstring(fnameIndexOf(/) + );

  string cname = fnameSubstring( fnameIndexOf());

  string className =;

  className = ClassLibraryCodeHandler;

  object h = null;

  try

  {

  //h = new TestHandler();

  h = ActivatorCreateInstance(TypeGetType(className));

  }

  catch (Exception e)

  {

  throw new HttpException(工廠不能為類型 + cname + 創建實例 e);

  }

  return (IHttpHandler)h;

  }

  public void ReleaseHandler(IHttpHandler handler)

  {

  }

  }(車延祿)

  配置文件

  <httpHandlers>

  <add verb=* path=defaultaspxdefaultaspx type=ClassLibraryTestHandlerFactoryClassLibrary></add>

  </httpHandlers>

  這樣TestHandlerFactory就會根據請求的不同頁面執行不同的HttpHandler處理程序了

  HttpHandler使用會話

  如果要在處理程序中使用Session那必須把該HttpHandler實現IRequiresSessionState接口IRequiresSessionState接口是個空接口它沒有抽象方法只是一個標記此處就不作例子驗證了

  寧可去碰壁也不在家面壁緣於自然順其自然


From:http://tw.wingwit.com/Article/program/net/201311/12275.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.