自定義IHttpModule實現URL重寫示例代碼

轉自周公的博客!

《ASP.NET夜話》第十二章中探討過ASP.NET底層運行機制的問題,在該書中本人也講到過了解一些ASP.NET的低層機制對於我們靈活控制ASP.NET有很大幫助,在該書中本人講述過如何用自定義的IHttpHandler來實現防盜鏈功能,由於篇幅限制在該書中沒有講述自定自定義IHttpModule可以實現什麼樣的效果,在本篇將講述利用自定義IHttpModule來實現URL地址重寫。
 一般來說,要顯示一些動態數據總是採用帶參數的方式,比如製作一個UserInfo.aspx的動態頁面用於顯示系統的UserInfo這個用戶信息表的數據,那麼需要在其後帶上一個參數來指定要顯示的用戶信息,比如UserInfo.aspx?UserId=1用於顯示錶中編號爲1的用戶的信息,如果爲2則顯示錶中編號爲2的用戶信息。在一些系統中我們可能看到的不是這樣的效果,可能會看到形如UserInfo2.aspx這樣的形式(當然形式可以多樣,只要有規律就行),當點擊這樣一個鏈接時看到的效果和UserInfo.aspx?UserId=2的效果一樣,這裏就用到了URL地址重寫的目的。在其它動態語言如asp、JSP、PHP中要實現URL地址重寫需要藉助於其它的手段(有一些現成可用的用於實現URL地址重寫的dll庫),使用這些第三方手段時需要對IIS做些配置,如果網站放置在購買的虛擬主機上可能就不能夠使用了(這種情況我以前就遇見過,當時曾經做過一個J2EE的網站放在虛擬主機上要對Tomcat做配置才能啓用數據庫連接池,但是虛擬主機提供商不提供這種服務),其實在ASP.NET中可以通過自定義IHttpModule就可以實現URL地址重寫。
 以下文字摘自《ASP.NET夜話》一書第十二章:
 “從客戶端向IIS發出請求到客戶端得到請求的結果這個過程中有一些複雜的操作,涉及到一些非託管的類和非託管的方法,這裏就不提了。在託管環境中第一個處理這個請求的類是ISAPIRuntime類。
 在ISAPIRuntime類中的ProcessRequest()方法中實例化HttpWorkerRequest類的實例,接着將HttpWorkerRequest的實例作爲參數交由HttpRuntime類處理,然後在HttpRuntime類中實例化HttpContext類的實例(這個HttpContext的實例中包含了當前HTTP請求的所有特定HTTP信息,爲實現了 IHttpModule 和 IHttpHandler 接口的類提供了對當前 HTTP 請求的 HttpContext 對象的引用,提供對請求的內部 Request、Response 和 Server 屬性的訪問),再將HttpContext的實例作爲參數交由HttpApplicationFactory類(此類是interal的,在外部程序集是不可見的,MSDN中也找不到此類的說明)用於實例化HttpApplication類的實例,在實例化HttpApplication類的實例的同時還會根據web.config中的配置(包括系統級和當前網站或虛擬目錄級)實例化所有實現IHttpModule接口的集合,這個集合是HttpModuleCollection類的實例,然後將HttpApplication類的實例作爲參數依次調用HttpModuleCollection中的每一個實現了IHttpModule接口的類的實例的Init()方法。
 在這個Init()方法中開始處理一系列HttpApplication類的實例的事件。

 上面的文字中講到在實例化HttpApplication類時會根據web.config中的配置(包括系統級和當前網站或虛擬目錄級)實例化所有實現IHttpModule接口的集合,然後會將HttpApplication類的實例作爲參數依次調用每個實現了IHttpModule接口的類的實例的Init()方法,在Init方法中可以添加對請求的特殊處理。在HttpApplication中有很多事件,其中第一個事件就是BeginRequest事件,在這個事件中我們可以對用戶請求的URL進行判斷,如果滿足某種要求,可以按另外一種方式來進行處理。
 比如,當接收到的用戶請求的URL是UserInfo(//d+).aspx這種形式時(這裏採用了正則表達式,表示的是UserInfo(數字).asp這種URL)我們將會運行UserInfo.aspx?UserId=(//d+)這樣一個URL,這樣網頁就能正常顯示了。
 當然實現URL地址重寫還需要藉助一個類:HttpContext。HttpContext類中定義了RewritePath 方法,這個方法有四種重載形式,分別是:
  RewritePath(String)  使用給定路徑重寫 URL。 
  RewritePath(String, Boolean)  使用給定路徑和一個布爾值重寫 URL,該布爾值用於指定是否修改服務器資源的虛擬路徑。 
  RewritePath(String, String, String)  使用給定路徑、路徑信息和一個布爾值重寫 URL,該布爾值用於指定是否修改服務器資源的虛擬路徑。 
  RewritePath(String, String, String, Boolean)  使用給定虛擬路徑、路徑信息、查詢字符串信息和一個布爾值重寫 URL,該布爾值用於指定是否將客戶端文件路徑設置爲重寫路徑。 
 對於這裏四個重載方法的區別我不一一詳細描述,因爲在這裏只用帶一個參數的重載方法就能滿足本文提出的要求。
 我們的步驟如下:
 首先編寫自定義IHttpModule實現,這個定義只定義了兩個方法Dispose()和Init()。在這裏我們可以不用關注Dispose()這個方法,這個方法是用來實現如何最終完成資源的釋放的。在Init方法中有一個HttpApplication參數,可以在方法中可以自定義對HttpApplication的事件處理方法。比如這裏我們的代碼如下:

  1. public void Init(HttpApplication context)  
  2. {  
  3.          //context.BeginRequest是開始處理HTTP管線請求時發生的事件  
  4.          context.BeginRequest += new EventHandler(context_BeginRequest);  
  5.          //context.Error是當處理過程中發生異常時產生的事件  
  6.          //context.Error += new EventHandler(context_Error);  
  7. }  


  然後在context_BeginRequest這個方法中自己編寫處理事件,我們編寫的代碼如下:

  1. void context_BeginRequest(object sender, EventArgs e)  
  2. {  
  3.          HttpApplication application = (HttpApplication)sender;  
  4.          HttpContext context = application.Context;  
  5.          HttpResponse response = context.Response;  
  6.          string path = context.Request.Path;  
  7.          string file = System.IO.Path.GetFileName(path);  
  8.          //重寫後的URL地址  
  9.          Regex regex=new Regex("UserInfo(//d+).aspx",RegexOptions.Compiled);  
  10.          Match match=regex.Match(file);  
  11.          //如果滿足URL地址重寫的條件  
  12.          if(match.Success)  
  13.          {  
  14.              string userId = match.Groups[1].Value;  
  15.              string rewritePath = "UserInfo.aspx?UserId=" + userId;  
  16.              //將其按照UserInfo.aspx?UserId=123這樣的形式重寫,確保能正常執行  
  17.              context.RewritePath(rewritePath);  
  18.          }  
  19. }  
 
  注意在上面的代碼中採用了正則表達式來進行匹配,使用正則表達式的好處就是在處理格式化文本時相當靈活。除此之外,我們在處理方法中僅僅對滿足要求的URL進行重寫,對於不滿足要求的URL則無需進行重寫,所以這樣就不會干擾沒有重寫的URL的正常運行(比如Index.aspx)。
 從那段從《ASP.NET夜話》摘出的話中可以看出,僅僅是編寫自己的IHttpModule實現還是不夠的,我們還需要讓處理Web請求的程序直到我們編寫的IHttpModule實現的存在,這就需要在web.config中配置。在本實例中只需要在本ASP.NET項目中的web.config節點中增加一個<httpModules></httpModules>節點(如果已存在此節點則可以不用添加),然後在此節點中增加一個配置即可,對於本實例,這個節點最終內容如下:
 
  1. <httpModules>  
  2.  <add name="UrlReWriter" type="UrlReWriter"/>  
  3.  </httpModules>  
 
 這樣我們的URL地址重寫就成功了。
 下面一個演示顯示用戶列表的鏈接的頁面設計代碼:

  1. <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>  
  2.    
  3.  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  4.    
  5.  <html xmlns="http://www.w3.org/1999/xhtml">  
  6.  <head runat="server">  
  7.      <title>無標題頁</title>  
  8.  </head>  
  9.  <body>  
  10.      <form id="form1" runat="server">  
  11.      <div>  
  12.      <ol>  
  13.      <li><a href="UserInfo1.aspx" mce_href="UserInfo1.aspx">編號爲1的用戶信息</a></li>  
  14.      <li><a href="UserInfo2.aspx" mce_href="UserInfo2.aspx">編號爲2的用戶信息</a></li>  
  15.      <li><a href="UserInfo3.aspx" mce_href="UserInfo3.aspx">編號爲3的用戶信息</a></li>  
  16.      <li><a href="UserInfo4.aspx" mce_href="UserInfo4.aspx">編號爲4的用戶信息</a></li>  
  17.      <li><a href="UserInfo.aspx?UserId=5" mce_href="UserInfo.aspx?UserId=5">編號爲5的用戶信息</a></li>  
  18.      </ol>  
  19.      </div>  
  20.      </form>  
  21.  </body>  
  22.  </html>  
 
  在UserInfo.aspx頁面的設計代碼如下:

  1. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="UserInfo.aspx.cs" Inherits="UserInfo" %>  
  2.    
  3.  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  4.    
  5.  <html xmlns="http://www.w3.org/1999/xhtml">  
  6.  <head runat="server">  
  7.      <title>無標題頁</title>  
  8.  </head>  
  9.  <body>  
  10.      <form id="form1" runat="server">  
  11.      <div>  
  12.        
  13.          <asp:Literal ID="Literal1" runat="server"></asp:Literal>  
  14.          <br />  
  15.      <img src="images/13.jpg" mce_src="images/13.jpg" width="283" height="485" title="MM" />  
  16.      </div>  
  17.      </form>  
  18.  </body>  
  19.  </html>  
 
  UserInfo.aspx頁面的對應邏輯代碼如下:

  1. using System;  
  2.  using System.Collections;  
  3.  using System.Configuration;  
  4.  using System.Data;  
  5.  using System.Web;  
  6.  using System.Web.Security;  
  7.  using System.Web.UI;  
  8.  using System.Web.UI.HtmlControls;  
  9.  using System.Web.UI.WebControls;  
  10.  using System.Web.UI.WebControls.WebParts;  
  11.    
  12.  public partial class UserInfo : System.Web.UI.Page  
  13.  {  
  14.      protected void Page_Load(object sender, EventArgs e)  
  15.      {  
  16.          if (!Page.IsPostBack)  
  17.          {  
  18.              string queryString = Request.QueryString["UserId"];  
  19.              int userId=0;  
  20.              if (int.TryParse(queryString, out userId))  
  21.              {  
  22.                  Literal1.Text = "這是編號爲" + userId.ToString() + "的用戶的信息。";  
  23.              }  
  24.              else  
  25.              {  
  26.                  Literal1.Text = "錯誤的參數";  
  27.              }  
  28.          }  
  29.      }  
  30.  }  
 
  程序的演示效果:
 程序的演示效果
 點擊進行了URL重寫的鏈接的效果(即UserInfo1.aspx):
 點擊進行了URL重寫的鏈接的效果(即UserInfo1.aspx)
 點擊沒有進行URL地址重寫的頁面的效果(即UserInfo.aspx?UserId=5):
 點擊沒有進行URL地址重寫的頁面的效果(即UserInfo.aspx?UserId=5)
 可見儘管我們進行了URL地址重寫並不會頁面的最終顯示效果,因爲最終實際執行的還是UserInfo.aspx?UserId=數字這種形式的URL。
 如果有興趣還可以對全站進行僞靜態化處理,所謂的僞靜態化就是所有的鏈接在瀏覽器地址欄裏看到的都是.html這樣的形式,實際上執行的仍是動態頁面,不過這就需要對IIS進行配置纔行。爲了簡單期間,本文中沒有使用數據庫,即使使用數據庫,只要重寫前的頁面能夠正常顯示,重寫之後是不會有什麼影響的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章