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

httpModules與httpHandlers

2013-11-13 10:30:22  來源: .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應用程序的輸出緩沖是如何設置的

  上面這麼多的事件我們看起來可能會有些眼暈但沒關系下面一步一步地看

  HttpModule生命周期示意圖

  

  下面是事件的觸發順序

  

  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接口是個空接口它沒有抽象方法只是一個標記此處就不作例子驗證了

  ASPNet處理Http Request時使用Pipeline(管道)方式由各個HttpModule對請求進行處理然後到達 HttpHandlerHttpHandler處理完之後仍經過Pipeline中各個HttpModule的處理最後將HTML發送到客戶端浏覽 器中

  生命周期中涉及到幾個非常重要的對象HttpHandlerHttpModuleIHttpHandlerFactory他們的執行(順序)大致的執行過程是這樣的client端發送頁面請求被IIS的某個進程截獲它根據申請的頁 面後綴(aspx)不同調用不同的頁面處理程序(asp>aspdll; aspx>ISAPIdll)而頁面處理程序在處理過程中則要經歷HttpModuleHttpHandler的處理前者HttpModule用於頁面處理前和處理後的一些事件的處理後者HttpHandler進行真正的頁面的處理

  如前所說HttpModule會在頁面處理前和後對頁面進行處理所以它不會影響真正的頁面請求通常用在給每個頁面的頭部或者尾部添加一些信息(如版 權聲明)等曾經見過一些免費的空間我們的頁面上傳上去後浏覽的時候發現在每個頁面的頭部和尾部多了很多小廣告如果理解了 HttpModule的原理要做這個就不是很難了~

  IHttpModule與IHttpHandler的區別整理

  先後次序先IHttpModule後IHttpHandler 注:Module要看你響應了哪個事件一些事件是在Handler之前運行的一些是在Handler之後運行的

  對請求的處理上:

  IHttpModule是屬於大小通吃類型無論客戶端請求的是什麼文件都會調用到它;例如aspxrarhtml的請求

  IHttpHandler則屬於挑食類型只有注冊過的文件類型(例如aspxasmx等等)才會輪到調用它

  IHttpHandler按照你的請求 生成響應的內容IHttpModule對請求進行預處理如驗證修改過濾等等同時也可以對響應進行處理

  ASPNet系統本身配置有很多HttpHandler和HttpModule以處理aspx等Net標准的頁面文件以及這些頁面文件中標 准的事件處理等查看%System%/MicrosoftNET\Framework\v\CONFIG目錄下的 nfig文件中的httpHandlers和httpModules節點可以看到這些配置如果有興趣可以使用Reflector查 看Net系統中相關的類和方法了解Net如何處理以及做了什麼處理

  Net也提供了一套機制來開發自定義的HttpHandler和 HttpModule均可以用於對HttpRequest的截取完成自定義的處理 HttpModule 繼承SystemWebIHttpModule接口實現自己的HttpModule類必須要實現接口的兩個方法Init和Dispose在 Init中可以添加需要截取的事件Dispose用於資源的釋放如果在Init中創建了自己的資源對象請在Dispose中進行釋放

  namespace MyModule

  {

  public class MyHttpModule : IHttpModule

  {

  public MyHttpModule()

  {

  }

  //Init方法用來注冊HttpApplication 事件

  public void Init(HttpApplication r_objApplication)

  {

  r_objApplicationBeginRequest += new EventHandler(thisBeginRequest);

  }

  public void Dispose()

  {

  }

  private void BeginRequest(object r_objSender EventArgs r_objEventArgs)

  {

  HttpApplication objApp = (HttpApplication)r_objSender;

  objAppResponseWrite(您請求的URL為 + objAppRequestPath);

  }

  }

  }

  將編譯的dll文件拷貝到web項目的bin目錄下在web項目的nfig文件systemweb節點中配置

  這樣就將自定義的HttpModule類MyHttpModule插入到了當前web的HttpModule的Pipeline中 HttpModule主要功能是對Application的各個事件進行截取在這些事件中完成自己的處理其實如果自己開發一些項目直接在 Globalasax中處理已經足夠了如果是開發一個Framework或者是某些方面的組件需要在事件中添加處理開發自定義的 HttpModule可以避免使用Framework或者組件時還得手工在Globalasax中添加代碼     目前想到的開發自定義HttpModule的用途有全局的身份/權限驗證自定義網站訪問/操作日志的記錄處於管理/調試等目的對站點進行監控追蹤 等當然如果是結合自定義的HttpHandler進行Framework的開發HttpModule可以用於其它的一些特殊的處理

  <httpModules>

  <add name=test type=MyHttpModuleTestMyHttpModuleMyHttpModule/>

  </httpModules>

  注意要區分大小寫因為nfig作為一個XML文件是大小寫敏感的type=MyHttpModuleTestMyHttpModuleMyHttpModule告訴我們

  系統將會將http request請求交給位於MyHttpModuledll文件中的MyHttpModuleTestMyHttpModule類去處理

  HttpHandler是完全的對Http Request的截取

  首先繼承SystemWebIHttpHandler接口實現自己的HttpHandler類必須要實現接口的ProcessRequest方 法和IsReusable屬性ProcessRequest方法中完成對每個Http Request的處理發送處理結果的HTML到輸出緩存中IsReusable屬性被Net Framework調用用以確定這個HttpHandler的實例是否可以被重用於同類型其它的Request處理

  如果你在自己的HttpHandler類中需要讀取或者是寫Session值需要再繼承一個接口IRequiresSessionState這個接 口沒有任何方法只是一個標記接口繼承這個接口之後就可以在自己的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 = contextResponse ;

  objResponseWrite(

  This request is handled by MyHttpHandler

  );

  }

  }

  }

  把編譯的dll文件拷貝到web項目的bin目錄下

  接下來這樣來測試一下MyHttpHandler我們為IIS配置一個為後綴名的文件類型用我們寫的MyHttpHandler來處理

  首先在IIS站點的Configuration配置裡面添加一個後綴名處理的Application Extention Mapping項

  然後在web項目的nfig節點節點中配置

  MyHttpHandler MyHandler/>

  verb屬性配置這個HttpHandler處理那些HTTP方法例如GETPOST等如果是處理所有方法就用*path屬性配置HttpHandler對哪些文件進行處理例如可以是如果是處理所有文件就用

  這樣這個站點上所類型文件的訪問都由MyHttpHandler處理使//localhost/站點虛擬目錄/訪問測試站點可以看到測試效果當然這個文件在Web服務器上是並不存在的

  對HttpHandler的使用比較典型的有Net的Web MVC開源項目MaverickMaverick使用一個Dispatcher類對所有的Http Request進行截取他以m作為後綴名向Web服務器提交請求在Dispatcher中m的後綴去掉提取Command Name然後以這個command name從配置文件中加載處理的flow形成一個chain依次對chain上的各個command和view進行處理對各個command和 view的處理結果可能會在chain中選擇不同的處理分支每個處理的Step中將處理結果的HTML寫入Response的緩存中進行輸出

  總體來說Maverick的框架架構概念很不錯但也存在明顯的缺陷以後有時間再詳細的寫寫它的架構和需要改進之處

  總之將HttpModuleHttpHandler以及使用Ajax等將客戶端進行封裝結合起來能夠給web項目的開發帶來非常大的改善空間

  AspNet HttpHandler實現URL重寫的

  我們經常看到很多網站訪問文章的時候才用的是**l 或***shtml (如本blog的日志訪問效果)其時這寫文件在服務器上不存在的那為什麼會出現這樣的效果呢是因為Web服務器上對URL執行了重寫把訪問的 URL根據特定的格式重寫成內部訪問頁面來實現的它的好處是便於用戶理解同時搜索引擎也能更好地收入你的網站當然其它的好處也很多這裡不做一一介 紹了

  本文所講的是使用AspNet中的HttpHandler實現URL重寫的它所實現的原理請看這裡本程序可以處理任何Url因為我在程序中使用了URL過慮只有訪問文件名是數字的才進行處理並指在內部執行一個新的頁面並輸出數據代碼如下

  public void ProcessRequest(HttpContext Context)

  {

  try {

  //申明Request

  HttpRequest Request = ContextRequest;

  //取來路Url的絕對路徑

  string Url = RequestUrlAbsolutePath;

  //取訪問的Web文件的開始字符間隔數

  int RegStart = UrlLastIndexOf(/) + ;

  //申明一個確定Web文件名是否全是數字

  Regex Reg = new Regex(@\d+);

  //用正則表達式進行匹配

  if (RegIsMatch(Url RegStart))

  {

  // 如果web文件名是數字則判定是查詢相關文章執行指定頁面             ContextServerExecute(~/PermaLinkaspx?id= + RegMatch(Url RegStart)Value);

  }

  }

  catch

  {

  ContextResponseRedirect(ContextRequestUrlToString());

  }

  }

  當然你首先要做的是先建一個類並繼承自IHttpHandler然後把這段代碼拷入並編譯在Web項目中若要使用此功能需要在nfig裡面加上如下語句

  <httpHandlers>

  <add verb=* path=*shtml type=HttpHandleUrlRewrite />

  </httpHandlers>

  同時還要在IIS中對Web項目進行配置在Web項目的屬性中在主目錄選項卡裡把執行權限改為腳本和可執行文件然後打開配置在應用程序擴展裡加上需重寫的文件格式的擴展好了成事具備只欠運行了


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