在ASP.NET中用MSDNURLRewriting實現Url Rewriting (二)

 1.10. 使用HttpModule來調用網址重寫
在ASP.NET一級來執行網址重寫,既可以使用HttpHandler,也可以使用HttpModule。當使用HttpModule的時候,必須決定如果該網址需要被重寫的話,究竟應該在整個請求的生命週期期間的那一個點來使用。乍一看着有些武斷,但是這個決定以重大而且微妙的方式影響到你的應用程序。之所以作出對網址重寫點的選擇是因爲內嵌的ASP.NET HttpModule使用Request對象的屬性值來完成自己的工作(回憶一下重寫路徑對Request對象的屬性值的改變),這些內嵌HttpModule和相應事件的密切關係列舉如下:
HttpModule 事件 簡介
FormsAuthenticationModule AuthenticateRequest 判斷用戶是否已通過表單授權方式獲取授權,如果沒有的話則將用戶重定向到指定的登錄頁面
FileAuthorizationModule AuthorizeRequest 當使用Windows授權方式的時候,該HttpModule判斷並確定該Microsoft Windows帳戶是否對其請求的資源擁有足夠的權限
UrlAuthorizationModule AuthorizeRequest 檢查並確認請求者是否對所訪問的網址擁有權限。該Url授權可以在web.config文件的<authorization>和<location>元素中配置

回想一下BeginRequest事件在AuthenticateRequest事件之前引發,而AuthenticateRequest事件又在AuthorizeRequest事件之前引發。
實現網址重寫的一個較爲安全的場合就是把它放在在BeginRequest事件中執行,這意味着如果要執行網址重寫的話,在衆多內嵌HttpModule運行的時候他已經完成了。這種途徑的最終用途淋漓盡致地體現在表單驗證上。當用戶訪問受限資源的時候,如果之前使用了表單驗證,他會自動被重定向到指定的登錄頁面,在成功登錄之後,用戶被重定向回先前試圖訪問的受限制頁面。
如果把網址重寫放在BeginRequest事件或者AuthenticateRequest事件中,在登錄頁面上執行提交後,該頁面會將用戶重定向到網址重寫指定的頁面。假定當用戶在瀏覽器上敲入/people/ScottMitchell.aspx地址,該地址是要被重定向到/info/employee.aspx?EmpID=1001的,如果該Web應用程序設定使用表單驗證,當用戶開始訪問/people/ScottMitchell.aspx的時候,該網址將重寫指向/info/employee.aspx?EmpID=1001,接着ForumAuthenticationModule啓動,如果需要的話將用戶重定向到登錄頁面,用戶登錄後重定向到的頁面將是/info/employee.aspx?EmpID=1001,這也是自從FormAuthenticationModule啓動運行時所發出請求的頁面。
同上類似,當把網址重寫放在BeginRequest事件或者AuthenticateRequest事件中運行的時候,UrlAuthenticationModule也發現了網址重寫指向的網址,這意味着如果在該應用程序的web.config文件中<location>節爲特定的網址配置特定的授權地址的話,你得引用重寫所指向的網址。
爲了解決這個微妙的問題,一個可能就是把網址重寫放在AuthorizeRequest事件中運行,但是在使用這種方法解決URL授權和表單授權的異常時又引入了一個新的缺陷:文件授權會失效。當使用Windows驗證的時候,FileAuthorizationModule檢查並驗證已通過驗證的用戶是否擁有足夠的權限訪問特定的ASP.NET頁面。
假定有一羣用戶並沒有Windows級別的訪問權限訪問C:/inetpub/wwwroot/info/employee.aspx,當這些用戶試圖訪問/info/employee.aspx?EmpID=1001的時候,他們會得到未授權的錯誤,如果我們把網址重寫放到AuthenticateRequest事件中運行,當FileAuthorizationModule驗證該安全性設置的時候,他仍任人爲被請求的文件是/people/ScottMitchell.aspx,而這時該網址已經被重寫了,因此FileAuthorizationModule會直接放行,讓用戶看到了網址重寫指向的內容:/info/employee.aspx?Empid=1001。
那麼什麼時候在HttpModule調用網址重寫合適呢?他決定於所使用的驗證方式,當然如果不使用驗證方式的話,那麼無論是在BeginRequest事件、AuthenticateRequest事件還是AuthorizeRequest事件中調用網址重寫沒有多大區別,如果使用表單驗證方式並且不使用Windows驗證方式的話,把網址重寫放入AuthorizeRequest事件委託中調用既可,如果使用Windows驗證方式的話,把這項功能放入BeginRequest事件或者AuthenticateRequest事件調用就行了。

1.11. 使用HttpHandler來調用網址重寫
除了上面所述方法外,網址重寫也可以放入HttpHandler或者HttpHandlerFactory中調用。HttpHandler是一個負責針對特定請求生成相應內容的類,而HttpHandlerFactory返回一個HTTP的實例,該實例針對特定請求生成相應內容。
本節將着眼於爲這些ASP.NET頁面創建一個網址重寫的HttpHandlerFactory。創建HttpHandlerFactory必須實現IHTTPHandlerFactory接口,它包括一個GetHandler()方法。ASP.NET引擎在初始化這些HttpModule後做出決定針對該請求調用相應的HttpHandler或者HttpHandlerFactory,在調用HttpHandlerFactory的時候,針對該Web請求以及隨同的其他信息的HttpContext中經過的的HttpHandlerFactory的GetHandler()方法將被ASP.NET引擎調用,HttpHandlerFactory必須返回一個能委託該請求的對象,並且該對象要能實現IHttpHandler接口。
要通過一個HttpHandler來調用網址重寫,可以先創建一個HttpHandlerFactory,它的GetHandler()方法檢查所請求的網址並決定是否需要調用網址重寫。如果要調用網址重寫的話則調用前文所述的已通過檢查的HttpContext對象的RewritePath()方法。最後該HttpHandlerFactory返回一個由類System.Web.UI.PageParser的GetCompiledInstance()方法返回的HttpHandler。(這與內嵌於ASP.NET頁面的HttpHandlerFactory(PageHandlerFactory)的工作原理相同。)
在所有HttpModule被初始化後,HttpHandlerFactory就開始被實例化。把網址重寫放在這些事件場所的最後一個裏頭調用的時候,也會碰到相同的問題:文件授權將會失效。如果非要依賴於Windows驗證和文件驗證的時候,你可能得使用HttpModule來調用網址重寫了。
下一章我們着眼於如何構建一個可重用的網址重寫引擎,使用下文所提的這些示例均以真實案例作爲參照,在作者主頁上提供下載。先用用一個簡單的網址重寫的例子來探討如何實現網址重寫,緊接着將利用網址重寫引擎中正則表達式的強大處理能力來展示真正“隱蔽”的網址重寫技術!

1.12. 使用網址重寫引擎實現簡單的網址重寫
爲了便於在Web應用程序中實現網址重寫,我構建了一個網址重寫引擎,該引擎提供下列功能:
 可以在web.config文件中爲頁面開發者定義其所使用的網址重寫引擎的規則;
 通過使用正則表達式來使所制定的網址重寫規則具有更加強大的重寫能力;
 能夠通過簡單配置即可在HttpModule和HttpHandler中使用網址重寫。
本節只探討通過HttpModule來實現網址重寫,要了解如何通過HttpHandler來實現網址重寫請下載本文提供的代碼。

1.12.1. 設置網址重寫引擎的配置信息
我們來探討一下在web.config中網址重寫規則的配置節。首先必須在web.config文件中指出是否需要在HttpHandler或者HttpModule中調用網址重寫,在web.config中,下文已經包含了兩個已經被註釋掉的配置節:

<!--
<HttpModules>
 <add type="URLRewriter.ModuleRewriter,URLRewriter" name="ModuleRewriter"/>
</HttpModules>
-->

<!--
<httpHandlers>
 <add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler,URLRewriter" />
</httpHandlers>
-->

被註釋掉的<HttpModules>爲配置使用HttpModule調用網址重寫;註釋掉的<httpHandler>爲配置使用HttpHandler調用網址重寫。
不論配置使用<HttpModules>還是<httpHandlers>調用網址重寫,除此之外還須配置網址重寫規則,一條重寫規則包括兩項字符串:請求URL中的查找模式和針對該模式的匹配成功後的替換字符串。該信息在web.config文件中用下列標籤描述:

<RewriterConfig>
 <Rules>
 <RewriterRule>
 <LookFor>pattern to look for</LookFor>
 <SendTo>String to replace pattern with </SendTo>
 </RewriterRule>
 <RewriterRule>
 <LookFor>pattern to look for</LookFor>
 <SendTo>String to replace pattern with </SendTo>
 </RewriterRule>
 
 </Rules>
</RewriterConfig>

每一條規則都用一個<RewriterRule>元素表示,以<LookFor>節表示查詢模式,當查詢模式發現匹配字符串時便用<SendTo>節表示的字符串進行替換。這些規則從上到下進行查詢匹配,如果找到一個匹配則按此規則執行網址重寫,並且停止查找。
配置<LookFor>節要使用正則表達式來進行字符串匹配和替換。(在此我們舉一個例子來說明如何使用正則表達式來對字符串進行匹配和替換。)既然該查找模式是一個正則表達式,那麼要注意避開對正則表達式保留字符串的直接使用。(正則表達式的保留字符串包括有:.,?,^,$,等等,可以通過在前面加上一個反斜線來引用這些保留字符,例如/.表示引用一個句點)
1.12.2. 使用HttpModule來執行網址重寫

創建一個HttpModule很簡單,只要創建一個實現IHttpModule接口的類,該IHttpModule接口定義了兩個方法:
 Init(HttpApplication),該方法在HttpModule初始化時引發,通過該方法爲HttpApplication事件調用相應的事件委託;
 Dispose(),當相應請求處理結束併發送回IIS調用此方法,通過此方法執行最終所有的清理和回收程序。
爲了更加方便地爲網址重寫創建HttpModule,從一開始我就創建一個抽象的基類(BaseModuleRewriter),該類實現了IHttpModule接口。在Init(HttpApplication)事件中,它通過BaseModuleRewriter_AuthorizeRequest方法引發了HttpApplication的AuthorizeRequest事件,該BaseModuleRewriter_AuthorizeRequest方法通過該類的Rewrite()方法重寫傳入參數HttpApplication對象的內部請求虛擬路徑(Path)。在BaseModuleRewriter對象中,該Rewrite()方法是抽象的,並且沒有實際內容,但在繼承自該類的對象中必須重載Rewrite()方法併爲該方法提供實際內容。
通過對該基類的繼承,所有需要做的工作就是創建一個繼承自BaseModuleRewriter的類,重載Rewrite()方法並在該方法中添加網址重寫邏輯代碼。下文列出BaseModuleRewriter代碼:

public abstract class BaseModuleRewriter : IHttpModule
{
 public virtual void Init(HttpApplication app){
 // WARNING! This does not work with Windows authentication!
 // If you are using Windows authentication,
 // change to app.BeginRequest
 app.AuthorizeRequest += new EventHandler(this.BaseModuleRewriter_AuthorizeRequest);
 }

 public virtual void Dispose() {}

 protected virtual void BaseModuleRewriter_AuthorizeRequest(object sender, EventArgs e){
 HttpApplication app = (HttpApplication) sender;
 Rewrite(app.Request.Path, app);
 }

 protected abstract void Rewrite(string requestedPath, HttpApplication app);
}
注意:該BaseModuleRewriter類將網址重寫放在AuthorizeRequest事件中調用,如果要使用Windows驗證並使用文件驗證模式時請修改代碼將網址授權放在BeginRequest或者AuthenticateRequest事件中。
ModuleRewriter繼承自BaseModuleRewriter,並真正意義地實現了網址重寫的操作,該類僅包含一個重載了的方法Rewrite(),其內容如下文所示:

protected override void Rewrite(string requestedPath, System.Web.HttpApplication app)
{
 // get the configuration rules
 RewriterRuleCollection rules = RewriterConfiguration.GetConfig().Rules;
 // iterate through each rule
 for(int i = 0; i < rules.Count; i++)
 {
 // get the pattern to look for, and
 // Resolve the Url (convert ~ into the appropriate directory)
 string lookFor = "^" +
 RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, rules[i].LookFor) + "$";
 // Create a regex (note that IgnoreCase is set)
 Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);
 // See if a match is found
 if (re.IsMatch(requestedPath))
 {
 // match found - do any replacement needed
 string sendToUrl = RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, re.Replace(requestedPath, rules[i].SendTo));
 // Rewrite the URL
 RewriterUtils.RewriteUrl(app.Context, sendToUrl);
 break; // exit the for loop
 }
 }
}

該Rewriter()方法以獲取web.config文件中的網址重寫規則的設置爲起始,它通過循環訪問各條網址重寫規則,每次均獲取當前規則中的LookFor屬性,用正則表達式驗證並判斷是否查找是否對當前請求的網址是否有匹配。
如果發現一條匹配,將用當前規則的SendTo值對請求的路徑執行一個正則表達式替換,替換後的地址通過參數的形式傳給RewriterUtils.RewriteUrl()方法,RewriterUtils是一個幫助類,它提供一對HttpModule和HttpHandler都可以使用的靜態方法,RewriterUrl()方法只是簡單地調用了HttpContext對象的RewritePath()方法。
注意:你已經注意到了當執行正則表達式匹配和替換的時候調用了一個RewriterUtils.ResolveUrl()方法。該幫助方法簡單地替換了應用程序路徑中“~”的所有實例。
本文附錄中提供所有涉及該網址重寫引擎的代碼下載,我們已經探討了主要的部分,但是還有其它一些組件諸如將web.config文件中XML格式化了的網址重寫規則反序列化至一個對象的類定義、通過HttpHandlerFactory實現網址重寫的類定義等。本文最後三節將通過一些真實案例來探討網址重寫的技術。
1.12.3. 用網址重寫引擎實現簡單的網址重寫
爲了更好地示範網址重寫引擎的運行,我們來建立一個ASP.NET Web應用程序來實現簡單的網址重寫引擎。假定我們爲一家在線銷售各類商品的公司服務,這些產品劃分爲以下類別:
分類編號(CategoryID) 分類名稱(CategoryName)
1 飲料(Beverages)
2 調味品(Condiments)
3 工藝品(Confections)
4 日記本(Diary Products)
... ...
假定已經建立好一個名爲ListProductsByCategoryID.aspx的ASP.NET頁面文件,它通過查詢參數獲取一個分類編號,並根據此編號獲取所有該分類下的所有商品。如果用戶想瀏覽所銷售的飲料類商品可以通過ListProductsByCategoryID.aspx?CategoryID=1來訪問,如果用戶想瀏覽所銷售的日記本類商品可以通過ListProductsByCategoryID.aspx?CategoryID=4來訪問。假定還有一個頁面ListCategories.aspx,它列出所有代售商品的分類編號。
顯然這裏發現了一個網址重寫的案例。對於用戶來說他們所輸入的地址不具有任何實際意義並且不具備任何“隱蔽性”,倒不如使用網址重寫引擎讓用戶去訪問/Products/Baverage.aspx地址,系統將該地址重寫到ListProductsByCategoryID.aspx?CategoryID=1。我們可以在web.config文件中來完成網址重寫任務:

<RewriterConfig>
 <Rules>
 <!—- Rules for products lister -->
 <RewriterRule>
 <LookFor>~/Products/Baverage.aspx</LookFor>
 <SendTo正則表達式驗證並判斷是否查找是否對當前請求的網址是否有匹配。
如果發現一條匹配,將用當前規則的SendTo值對請求的路徑執行一個正則表達式替換,替換後的地址通過參數的形式傳給RewriterUtils.RewriteUrl()方法,RewriterUtils是一個幫助類,它提供一對HttpModule和HttpHandler都可以使用的靜態方法,RewriterUrl()方法只是簡單地調用了HttpContext對象的RewritePath()方法。
注意:你已經注意到了當執行正則表達式匹配和替換的時候調用了一個RewriterUtils.ResolveUrl()方法。該幫助方法簡單地替換了應用程序路徑中“~”的所有實例。
本文附錄中提供所有涉及該網址重寫引擎的代碼下載,我們已經探討了主要的部分,但是還有其它一些組件諸如將web.config文件中XML格式化了的網址重寫規則反序列化至一個對象的類定義、通過HttpHandlerFactory實現網址重寫的類定義等。本文最後三節將通過一些真實案例來探討網址重寫的技術。
1.12.3. 用網址重寫引擎實現簡單的網址重寫
爲了更好地示範網址重寫引擎的運行,我們來建立一個ASP.NET Web應用程序來實現簡單的網址重寫引擎。假定我們爲一家在線銷售各類商品的公司服務,這些產品劃分爲以下類別:
分類編號(CategoryID) 分類名稱(CategoryName)
1 飲料(Beverages)
2 調味品(Condiments)
3 工藝品(Confections)
4 日記本(Diary Products)
... ...
假定已經建立好一個名爲ListProductsByCategoryID.aspx的ASP.NET頁面文件,它通過查詢參數獲取一個分類編號,並根據此編號獲取所有該分類下的所有商品。如果用戶想瀏覽所銷售的飲料類商品可以通過ListProductsByCategoryID.aspx?CategoryID=1來訪問,如果用戶想瀏覽所銷售的日記本類商品可以通過ListProductsByCategoryID.aspx?CategoryID=4來訪問。假定還有一個頁面ListCategories.aspx,它列出所有代售商品的分類編號。
顯然這裏發現了一個網址重寫的案例。對於用戶來說他們所輸入的地址不具有任何實際意義並且不具備任何“隱蔽性”,倒不如使用網址重寫引擎讓用戶去訪問/Products/Baverage.aspx地址,系統將該地址重寫到ListProductsByCategoryID.aspx?CategoryID=1。我們可以在web.config文件中來完成網址重寫任務:

<RewriterConfig>
 <Rules>
 <!—- Rules for products lister -->
 <RewriterRule>
 <LookFor>~/Products/Baverage.aspx</LookFor>
 <SendTo>~/ListProductsByCategoryID.aspx?CategoryID=1</SendTo>
 </RewriterRule>
 </Rules>
</RewriterConfig>
很明顯地看到,搜索用戶訪問的路徑是否匹配/Products/Baverage.aspx,如果匹配的話,則將網址重寫到/ListProductsByCategoryID.aspx?CategoryID=1。
注意:你會發現<LookFor>節點中避免直接在“Baverage.aspx”中使用句點“.”是因爲<LookFor>節點的值是正則表達式的匹配模式,在正則表達式中句點符號是一個特殊字符,它表示匹配任何一個字符,也就是說如果訪問BaverageQaspx時也會發生匹配,爲了避免發生這個句點引起的匹配我們得在該句點符號前面加上一個“/”,表示引用句點符號
通過該規則定義,當用戶訪問/Products/Baverage.aspx文件的時候,他們將看到代售的飲料類商品列表信息。圖3爲訪問/Products/Baverage.aspx地址時的瀏覽器截圖,注意在瀏覽器中地址欄上顯示的是用戶輸入的/Products/Baverage.aspx地址,但是實際訪問的地址卻是網址重寫後的/ListProductsByCategoryID.aspx?CategoryID=1。(事實上,在服務器上根本就不存在/Products/Baverage.aspx文件!)

圖三.網址重寫後的對商品分類的請求

和/Products/Baverage.aspx類似,下一步我們添加其它分類的重寫規則,只需簡單地在web.config文件中<Rules>中在添加其他<RewriteRule>節即可。該演示完整的重寫規則集合請參考下載文檔的web.config文件中的定義。
爲了讓該網址更具有“隱蔽性”,如果讓用戶把/Products/Baverage.aspx後面Baverage.aspx一段截去,在瀏覽器中輸入/Products/來瀏覽產品分類列表會更好一些。乍一看,這項任務微不足道,只需添加一條網址重寫規則將/Products/映射到/ListCategories.aspx即可。然而這裏有一個微妙之處,你必須先創建一個/Products/目錄,並在裏面放一個空文件Default.aspx。
要認識爲什麼這些額外的步驟是必須的,先回顧一下前文。網址重寫引擎是處於ASP.NET一級的,也就是說,如果ASP.NET沒有獲得處理請求的機會的話,網址重寫引擎就不能對輸入的網址請求作出判斷。此外,IIS僅在請求文件包含相應擴展名時纔將請求轉交給ASP.NET引擎。如果用戶訪問/Products/,IIS並不知道其擴展名是什麼,於是它檢查該目錄下的文件看是否包含有默認首頁文件名(Default.aspx,Default.htm,Default.asp,等等,這些文件名在IIS管理工具對話框中Web服務器屬性對話框中的文檔標籤中定義。)當然,如果/Products/目錄不存在的話,IIS將返回一個HTTP 404錯誤。
所以我們需要創建一個/Products/目錄並在該目錄下額外創建一個空文件Default.aspx,IIS會檢查該目錄下的文件,發現有一個默認文件名Default.aspx,於是將請求轉交給ASP.NET,這樣,網址重寫引擎才能生效。

<RewriterRule>
 <LookFor>~/Products/Default.aspx</LookFor>
 <SendTo>~ListCategories.aspx</SendTo>
</RewriterRule>
通過該規則,用戶訪問/Products/Default.aspx或者訪問/Products/都可以看到如圖四所示的產品分類列表。

圖四.在網址上添加“隱蔽性”


本文來自: 腳本之家(www.jb51.net) 詳細出處參考:http://www.jb51.net/article/8035.htm

發佈了2 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章