使用 HTTP 模組執行 URL 重寫

ForTechnology發表於2011-08-17
例如,假設我們的網站中包含每個員工通過 /info/employee.aspx?empID=employeeID 均可訪問的資訊。為了使 URL 可以更多地被“刪節”,我們可以決定通過以下地址來訪問員工頁面:/people/EmployeeName.aspx。這就是我們要使用 URL 重寫的一個例子。也就是說,在請求 /people/ScottMitchell.aspx 頁面時,我們要重寫該 URL,以便使用 /info/employee.aspx?empID=1001 頁面。

使用 HTTP 模組執行 URL 重寫
在 ASP.NET 級別執行 URL 重寫時,可以使用 HTTP 模組或 HTTP 處理程式來執行重寫。使用 HTTP 模組時,必須決定在請求有效期內的哪個時間點上來檢查 URL 是否需要重寫。乍一看,這似乎可以任意選擇,但決定會以一種明顯而微妙的方式對應用程式產生影響。由於內建 ASP.NET HTTP 模組使用 Request 物件的屬性執行任務,因此選擇在何處執行重寫非常重要。(如上所述,重寫路徑將改變 Request 物件的屬性值。)下面列出了這些密切相關的內建 HTTP 模組及其捆綁到的事件:

HTTP 模組 事件 說明
FormsAuthenticationModule
AuthenticateRequest
確定使用者是否通過了窗體身份驗證。如果沒有,使用者將被自動復位向到指定的登入頁面。

FileAuthorizationMoudle
AuthorizeRequest
使用 Windows 身份驗證時,此 HTTP 模組將檢查以確保 Microsoft® Windows® 帳戶對被請求的資源具有足夠的許可權。

UrlAuthorizationModule
AuthorizeRequest
檢查以確保請求者可以訪問指定的 URL。通過 Web.config 檔案中的 元素來指定 URL 授權。


如上所述,BeginRequest 事件在 AuthenticateRequest 之前觸發,後者在 AuthenticateRequest 之前觸發。

可以執行 URL 重寫的一個安全位置是在 BeginRequest 事件中。也就是說,如果 URL 需要重寫,該操作將在任何一個內建 HTTP 模組執行後執行。使用窗體身份驗證時,這種方法存在一定的缺陷。如果您以前使用過窗體身份驗證,您會了解當使用者訪問受限資源時,他們將被自動復位向到指定的登入頁面。成功登入後,使用者將被返回到他們第一次嘗試訪問的頁面。

如果在 BeginRequest 或 AuthenticateRequest 事件中執行 URL 重寫,登入頁面(提交後)將把使用者復位向到重寫後的頁面上。也就是說,假設使用者在其瀏覽視窗中鍵入了 /people/ScottMitchell.aspx,此地址將被重寫為 /info/employee.aspx?empID=1001。如果將 Web 應用程式配置為使用窗體身份驗證,當使用者第一次訪問 /people/ScottMitchell.aspx 時,首先,URL 將被重寫為 /info/employee.aspx?empID=1001;接下來,FormsAuthenticationModule 將執行,並將使用者復位向到登入頁面(如果需要)。但是,使用者在成功登入後將被髮送到 /info/employee.aspx?empID=1001,因為當 FormsAuthenticationModule 執行後,此 URL 即是請求的 URL。

同樣,在 BeginRequest 或 AuthenticateRequest 事件中執行重寫時,UrlAuthorizationModule 看到的將是重寫後的 URL。也就是說,如果您在 Web.config 檔案中使用 元素來為特定的 URL 指定授權,則必須引用重寫後的 URL。

要解決這些細微問題,您可以決定在 AuthorizeRequest 事件中執行 URL 重寫。此方法解決了 URL 授權和窗體身份驗證的一些問題,但同時也產生了新的問題:檔案授權無法工作。使用 Windows 身份驗證時,FileAuthorizationModule 將檢查以確保通過身份驗證的使用者具有訪問特定 ASP.NET 頁面的相應許可權。

假設一組使用者對 C:\Inetput\wwwroot\info\employee.aspx 沒有 Windows 級別的檔案訪問許可權,並要嘗試訪問 /info/employee.aspx?empID=1001,他們將會收到授權錯誤訊息。但是,如果我們將 URL 重寫移到 AuthenticateRequest 事件中,當 FileAuthorizationModule 檢查安全設定時,仍然認為被請求的檔案是 people/ScottMitchell.aspx,因為該 URL 必須被重寫。因此,檔案授權檢查將通過,允許此使用者檢視重寫後的 URL /info/employee.aspx?empID=1001 的內容。

那麼,應該何時在 HTTP 模組中執行 URL 重寫?這取決於要使用的身份驗證型別。如果不想使用任何身份驗證,則無論 URL 重寫發生在 BeginRequest、AuthenticateRequest 還是 AuthorizeRequest 中都沒有什麼關係。如果要使用窗體身份驗證而不使用 Windows 身份驗證,請將 URL 重寫放在 AuthorizeRequest 事件處理程式中執行。最後,如果要使用 Windows 身份驗證,請在 BeginRequest 或 AuthenticateRequest 事件進行過程中安排 URL 重寫。

在 HTTP 處理程式中執行 URL 重寫
也可以由 HTTP 處理程式或 HTTP 處理程式工廠執行 URL 重寫。如上所述,HTTP 處理程式是負責生成特定型別請求的內容的類;HTTP 處理程式工廠是負責返回 HTTP 處理程式例項的類,該例項可以生成特定型別請求的內容。

在本文中,我們將對如何為 ASP.NET 網頁建立 URL 重寫 HTTP 處理程式工廠進行討論。HTTP 處理程式工廠必須實現 IHttpHandlerFactory 介面,此介面包括 GetHandler() 方法。初始化相應的 HTTP 模組後,ASP.NET 引擎將確定為給定的請求呼叫哪個 HTTP 處理程式或 HTTP 處理程式工廠。如果要呼叫 HTTP 處理程式工廠,ASP.NET 引擎將為 Web 請求呼叫傳入 HttpContext 的 HTTP 處理程式工廠的 GetHandler() 方法,以及一些其他資訊。然後,HTTP 處理程式工廠必須返回一個物件,該物件將實現可以處理請求的 IHttpHandler。

要通過 HTTP 程式程式執行 URL 重寫,我們可以建立一個 HTTP 處理程式工廠,該處理程式工廠的 GetHandler() 方法將檢查被請求的路徑,以確定是否需要重寫 URL。如果需要,它可以呼叫傳入的 HttpContext 物件的 RewritePath() 方法,如前面所討論的。最後,HTTP 處理程式工廠可以返回由 System.Web.UI.PageParser 類的 GetCompiledPageInstance() 方法返回的 HTTP 處理程式。(此技術與內建 ASP.NET 網頁 HTTP 處理程式工廠 PageHandlerFactory 工作時所應用的技術相同。)

由於所有 HTTP 模組都將在例項化自定義 HTTP 處理程式工廠之前進行初始化,因此,在將 URL 重寫放在事件的後半段時,使用 HTTP 處理程式工廠就會帶來相同的風險,即檔案授權無法工作。因此,如果您依賴於 Windows 身份驗證和檔案授權,您可能希望為 URL 重寫使用 HTTP 模組方法。

在下一部分中,我們將對構建可重用的 URL 重寫引擎進行討論。在介紹了 URL 重寫引擎(可通過下載本文的程式碼獲得)之後,我們將在剩下的兩個部分中對 URL 重寫的實際使用情況進行介紹。首先,我們將討論如何使用 URL 重寫引擎,並介紹一個簡單的 URL 重寫示例。接下來,我們將利用重寫引擎的正規表示式功能來提供真正“可刪節”的 URL。

返回頁首
構建 URL 重寫引擎
為了有助於描述如何在 ASP.NET Web 應用程式中實現 URL 重寫,我建立了 URL 重寫引擎。此重寫引擎將提供以下功能:

• 使用 URL 重寫引擎的 ASP.NET 頁面開發人員可以在 Web.config 檔案中指定重寫規則。

• 重寫規則可以使用正規表示式來實現功能強大的重寫規則。

• 可以輕鬆地將 URL 重寫配置為使用 HTTP 模組或 HTTP 處理程式。


在本文中,我們將介紹僅使用 HTTP 模組的 URL 重寫。要檢視如何使用 HTTP 處理程式來執行 URL 重寫,請參考可隨本文下載的程式碼。

為 URL 重寫引擎指定配置資訊
讓我們先介紹一下 Web.config 檔案中重寫規則的結構。首先,您需要在 Web.config 檔案中指明要使用 HTTP 模組還是 HTTP 處理程式來執行 URL 重寫。在下載程式碼中,Web.config 檔案包含兩個已註釋掉的條目:

<!--

name="ModuleRewriter" />

--&gt

<!--

type="URLRewriter.RewriterFactoryHandler, URLRewriter" />

--&gt

註釋掉 條目,以使用 HTTP 模組執行重寫;註釋掉 條目,以使用 HTTP 處理程式執行重寫。

除了指定使用 HTTP 模組還是 HTTP 處理程式執行重寫外,Web.config 檔案還包含重寫規則:重寫規則由兩個字串組成:要在被請求的 URL 中查詢的模式;要替換此模式的字串(如果找到)。在 Web.config 檔案中,此資訊是使用以下語法表達的:




要查詢的模式
要用來替換模式的字串


要查詢的模式
要用來替換模式的字串

...



每個重寫規則均由 元素表達。要搜尋的模式由 元素指定,而要替換所找到的模式的字串將在 元素中輸入。這些重寫規則將從頭到尾進行計算。如果發現與某個規則匹配,URL 將被重寫,並且對重寫規則的搜尋將會終止。

元素中指定模式時,請注意,要使用正規表示式來執行匹配和字串替換。(稍後,我們將介紹一個真實的示例,說明如何使用正規表示式來搜尋模式。)由於模式是正規表示式,應確保轉義正規表示式中的任何保留字元。(一些正規表示式保留字元包括:.、?、^、$ 及其他。可以通過在前面加反斜槓(如 \.)對這些字元進行轉義,以匹配文字句點。)

使用 HTTP 模組執行 URL 重寫
建立 HTTP 模組與建立可以實現 IHttpModule 介面的類一樣簡單。IHttpModule 介面定義了兩種方法:

• Init(HttpApplication)。此方法在初始化 HTTP 模組後觸發。在此方法中,您將把事件處理程式繫結到相應的 HttpApplication 事件。

• Dispose()。當請求已完成並已傳送回 IIS 時呼叫此方法。您應當在此處執行所有最終的清除操作。


為了便於為 URL 重寫建立 HTTP 模組,我將從建立抽象基類 BaseModuleRewriter 開始介紹。此類將實現 IHttpModule。在 Init() 事件中,它將 HttpApplication 的 AuthorizeRequest 事件繫結到 BaseModuleRewriter_AuthorizeRequest 方法。BaseModuleRewriter_AuthorizeRequest 方法將呼叫該類傳入被請求的 Path 的 Rewrite() 方法,以及傳入 Init() 方法的 HttpApplication 物件。Rewrite() 方法是抽象的,也就是說,在 BaseModuleRewriter 類中,Rewrite() 方法沒有方法主體;從 BaseModuleRewriter 派生而來的類必須覆蓋此方法並提供方法主體。

具有此基類後,只需建立由 BaseModuleRewriter 派生的類即可,該類可以覆蓋 Rewrite() 並在那裡執行 URL 重寫邏輯。下面顯示了 BaseModuleRewriter 的程式碼。

public abstract class BaseModuleRewriter : IHttpModule
{
public virtual void Init(HttpApplication app)
{
// 警告!此程式碼不適用於 Windows 身份驗證!
// 如果使用 Windows 身份驗證,
// 請改為 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 事件中執行 URL 重寫。如上所述,如果將 Windows 身份驗證與檔案授權結合使用,您需要對此做出更改,以便可以在 BeginRequest 或 AuthenticateRequest 事件中執行 URL 重寫。

ModuleRewriter 類擴充套件了 BaseModuleRewriter 類,並負責執行實際的 URL 重寫。ModuleRewriter 包含單一覆蓋方法(Rewrite()),如下所示:

protected override void Rewrite(string requestedPath,
System.Web.HttpApplication app)
{
// 獲得配置規則
RewriterRuleCollection rules =
RewriterConfiguration.GetConfig().Rules;

// 遍歷每個規則...
for(int i = 0; i < rules.Count; i++)
{
// 獲得要查詢的模式,並且
// 解析 Url(轉換為相應的目錄)
string lookFor = "^" +
RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath,
rules[i].LookFor) + "$";

// 建立 regex(請注意,已設定 IgnoreCase...)
Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);

// 檢視是否找到了匹配的規則
if (re.IsMatch(requestedPath))
{
// 找到了匹配的規則 -- 進行必要的替換
string sendToUrl =
RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath,
re.Replace(requestedPath, rules[i].SendTo));

// 重寫 URL
RewriterUtils.RewriteUrl(app.Context, sendToUrl);
break; // 退出 For 迴圈
}
}
}

Rewrite() 方法從獲取 Web.config 檔案中的一組重寫規則開始。然後,它將遍歷重寫規則,每次遍歷一個,對於每個規則,它將獲取規則的 LookFor 屬性,並使用正規表示式來確定是否在被請求的 URL 中找到了匹配的規則。

如果找到了匹配的規則,將在具有 SendTo 屬性值的被請求路徑上執行正規表示式替換。然後,替換後的 URL 將被傳遞到 RewriterUtils.RewriteUrl() 方法中。RewriterUtils 是一個 helper 類,此類將提供一對由 URL 重寫 HTTP 模組和 HTTP 處理程式使用的靜態方法。RewriterUrl() 方法僅呼叫 HttpContext 物件的 RewriteUrl() 方法。

注意:您可能已注意到,執行正規表示式匹配和替換時,將呼叫 RewriterUtils.ResolveUrl()。此 helper 方法只替換具有應用程式路徑值的字串中的所有 ~ 例項。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-705171/,如需轉載,請註明出處,否則將追究法律責任。

相關文章