ASP.NET中執行URL重寫

 

可以使用 ISAPI 篩選器在 IIS Web 服務器級別實現 URL 重寫,也可以使用 HTTP 模塊或 HTTP 處理程序在 ASP.NET 級別實現 URL 重寫。本文重點介紹如何使用 ASP.NET 實現 URL 重寫,因此我們將不對使用 ISAPI 篩選器實現 URL 重寫的細節進行深入探討。但是,有大量的第三方 ISAPI 篩選器可用於 URL 重寫,例如:

通過 System.Web.HttpContext 類的 RewritePath() 方法,可以在 ASP.NET 級別實現 URL 重寫。HttpContext 類包含有關特定 HTTP 請求的 HTTP 特定信息。對於 ASP.NET 引擎收到的每個請求,均爲該請求創建一個 HttpContext 實例。此類具有如下屬性:RequestResponse ,提供對傳入請求和傳出響應的訪問;ApplicationSession ,提供對應用程序和會話變量的訪問;User ,提供有關通過了身份驗證的用戶的信息;其他相關屬性。

使用 RewritePath() 方法可以接受單個字符串作爲要使用的新路徑。HttpContext 類的 RewritePath(string) 方法在內部對 Request 對象的 Path 屬性和 QueryString 屬性進行更新。除了 RewritePath(string) ,.NET Framework 1.1 還包括另一種形式的 RewritePath() 方法,此方法可以接受三個字符串輸入參數。此備用重載形 式不僅要設置 Request 對象的 Path 屬性和 QueryString 屬性,還要設置內部成員變量,這些變量用於計算 Request 對象的 PhysicalPathPathInfoFilePath 屬性值。

要在 ASP.NET 中實現 URL 重寫,需要創建 HTTP 模塊或 HTTP 處理程序,以便完成以下操作:

  1. 檢查被請求的路徑,以確定 URL 是否需要重寫。

  2. 如果需要重寫,通過調用 RewritePath() 方法來重寫路徑。

例如,假設我 們的網站中包含每個員工通過 /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 文件中的 <authorization>

和 <location> 元素來指定 URL 授權。

 

 

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

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

如果在 BeginRequestAuthenticateRequest 事件中執行 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。

同樣,在 BeginRequestAuthenticateRequest 事件中執行重寫時,UrlAuthorizationModule 看到的將是重寫後的 URL。也就是說,如果您在 Web.config 文件中使用 <location> 元素來爲特定的 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 重寫發生在 BeginRequestAuthenticateRequest 還是 AuthorizeRequest 中都沒有什麼關係。如果要使用窗體身份驗證而不使用 Windows 身份驗證,請將 URL 重寫放在 AuthorizeRequest 事件處理程序中執行。最後,如果要使用 Windows 身份驗證,請在 BeginRequestAuthenticateRequest 事件進行過程中安排 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 文件包含兩個已註釋掉的條目:

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

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

註釋掉 <httpModules> 條目,以使用 HTTP 模塊執行重寫;註釋掉 <httpHandlers> 條目,以使用 HTTP 處理程序執行重寫。

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

<RewriterConfig>
<Rules>
<RewriterRule>
<LookFor>要查找的模式</LookFor>
<SendTo>要用來替換模式的字符串</SendTo>
</RewriterRule>
<RewriterRule>
<LookFor>要查找的模式</LookFor>
<SendTo>要用來替換模式的字符串</SendTo>
</RewriterRule>
...
</Rules>
</RewriterConfig>

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

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

使用 HTTP 模塊執行 URL 重寫

創建 HTTP 模塊與創建可以實現 IHttpModule 接口的類一樣簡單。IHttpModule 接口定義了兩種方法:

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

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

爲了便於爲 URL 重寫創建 HTTP 模塊,我將從創建抽象基類 BaseModuleRewriter 開始介紹。此類將實現 IHttpModule 。在 Init() 事件中,它將 HttpApplicationAuthorizeRequest 事件綁定到 BaseModuleRewriter_AuthorizeRequest 方法。BaseModuleRewriter_AuthorizeRequest 方法將調用該類傳入被請求的 PathRewrite() 方法,以及傳入 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 身份驗證與文件授權結合使用,您需要對此做出更改,以便可以在 BeginRequestAuthenticateRequest 事件中執行 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 方法只替換具有應用程序路徑值的字符串中的所有 ~ 實例。

URL 重寫引擎的整個代碼可隨本文下載。我們已經介紹了大部分密切相關的組件,但還有一些其他組件(例如,對 Web.config 文件中 XML 格式的重寫規則進行反序列化以使其成爲對象的類),以及用於 URL 重寫的 HTTP 處理程序工廠。本文剩餘的三個部分將對 URL 重寫的實際使用情況進行介紹。

使用 URL 重寫引擎執行簡單的 URL 重寫

爲了實際演示 URL 重寫引擎,我們來構建一個使用簡單 URL 重寫的 ASP.NET Web 應用程序。假設我們所工作的公司通過網絡銷售分類產品。這些產品分爲以下幾個類別:

類 別 ID

類別名稱

1

飲料

2

調味品

3

糖果

4

奶製品

...

...

假設我們已創建了名爲 ListProductsByCategory.aspx 的 ASP.NET 網頁,該網頁在查詢字符串中接受類別 ID 值,並顯示屬於該類的所有產品。因此,要查看我們銷售的飲料的用戶可以訪問 ListProductsByCategory.aspx?CategoryID=1,而那些要查看奶製品的用戶可以訪問 ListProductsByCategory.aspx?CategoryID=4。此外,還假設我們有一個名爲 ListCategories.aspx 的頁面,該頁面列出了待售的所有產品類別。

很顯然,這是一個 URL 重寫事例,因爲提供給用戶的 URL 沒有爲用戶帶來任何意義,也沒有爲他們提供任何“可刪節性”。因此,讓我們使用 URL 重寫,以便在用戶訪問 /Products/Beverages.aspx 時,他們的 URL 將被重寫爲 ListProductsByCategory.aspx?CategoryID=1。我們可以在 Web.config 文件中使用以下 URL 重寫規則來實現此功能。

<RewriterConfig>
<Rules>
<!-- 產品製表者規則 -->
<RewriterRule>
<LookFor>~/Products/Beverages/.aspx</LookFor>
<SendTo>~/ListProductsByCategory.aspx?CategoryID=1</SendTo>
</RewriterRule>
<RewriterRule>
</Rules>
</RewriterConfig>

正如您可以看到的,此規則將進行搜索,以查看用戶請求的路徑是否爲 /Products/Beverages.aspx。如果是,它便將 URL 重寫爲 /ListProductsByCategory.aspx?CategoryID=1。

注意 :請注意,<LookFor > 元素對 Beverages.aspx 中的句點進行了轉義。這是因爲在正則表達式模式中使用了 <LookFor > 值,並且句點是正則表達式中的特殊字符,該字符表示“匹配任意字符”,例如,與 URL /Products/BeveragesQaspx 匹配。通過轉義句點(使用 /.),可以表明我們要匹配的是文字句點,而不是任何舊的字符。

有了此規則之後,當用戶訪問 /Products/Beverages.aspx 時,頁面上將顯示待售的飲料。圖 3 顯示了訪問 /Products/Beverages.aspx 的瀏覽器的快照。請注意,在瀏覽器的地址欄中,URL 將讀取 /Products/Beverages.aspx,但用戶實際看到的是 ListProductsByCategory.aspx?CategoryID=1 的內容。(實際上,Web 服務器上根本不存在 /Products/Beverages.aspx 文件!)

圖 3. 重寫 URL 之後請求類別

與 /Products/Beverages.aspx 相似,下面我們要爲其他產品類別添加重寫規則。此操作僅包括在 Web.config 文件的 <Rules> 元素內添加附加的 <RewriterRule > 元素。請參閱下載內容中的 Web.config 文件,以獲取用於此演示的一組完整的重寫規則。

爲了使 URL 更具可刪節性,最好使用戶只需從 /Products/Beverages.aspx 中刪除 Beverages.aspx 即可看到產品類別的列表。乍一看,這可能是一項很普通的任務(只需添加一個將 /Products/ 映射到 /ListCategories.aspx 的重寫規則即可)。但此操作存在一個微妙之處,即您必須首先創建一個 /Products/ 目錄,並在 /Products/ 目錄中添加一個空的 Default.aspx 文件。

要理解需要執行這些額外步驟的原因,可以參考前面的內容,即 URL 重寫引擎位於 ASP.NET 級別上。也就是說,如果 ASP.NET 引擎永遠沒有機會處理請求,URL 重寫引擎就沒有辦法檢測傳入的 URL。而且,請記住,僅當被請求的文件具有相應的擴展名時,IIS 纔會將傳入請求傳遞給 ASP.NET 引擎。因此,如果用戶訪問 /Products/,而 IIS 沒有看到任何文件擴展名,那麼它將檢查目錄,以查看是否存在這樣一個文件,即該文件名爲默認文件名中的一個。(Default.aspx、 Default.htm、Default.asp 等等。“IIS 管理”對話框中“Web 服務器屬性”對話框的“文檔”選項卡對這些默認文件名進行了定義。)當然,如果 /Products/ 目錄不存在,IIS 將返回 HTTP 404 錯誤。

因此,我們需要創建 /Products/ 目錄。另外,我們還需要在此目錄中創建一個文件 Default.aspx。這樣,當用戶訪問 /Products/ 時,IIS 將檢測目錄,查看是否存在一個名爲 Default.aspx 的文件,然後將處理過程傳遞給 ASP.NET 引擎。然後,URL 重寫器將在重寫 URL 時分解。

創建目錄和 Default.aspx 文件後,請繼續操作,並向 <Rules > 元素中添加以下重寫規則:

<RewriterRule>
<LookFor>~/Products/Default/.aspx</LookFor>
<SendTo>~/ListCategories.aspx</SendTo>
</RewriterRule>

有了此規則之後,當用戶訪問 /Products/ 或 /Products/Default.aspx 時,他們將看到產品類別列表,如圖 4 所示。

4. 向 URL 添加“可刪節性”

處理回發

如果要 重寫的 URL 中包含一個服務器端的 Web 窗體並執行回發,則窗體回發後,將使用帶下劃線的 URL。也就是說,如果用戶在瀏覽器中輸入 /Products/Beverages.aspx,他們在瀏覽器地址欄中看到的將是 /Products/Beverages.aspx,但是他們看到的內容將是 ListProductsByCategory.aspx?CategoryID=1 的內容。如果 ListProductsByCategory.aspx 執行了回發,用戶將被回發到 ListProductsByCategory.aspx?CategoryID=1,而不是 /Products/Beverages.aspx。這樣不會中斷任何內容,但從用戶的角度考慮,如果單擊按鈕時突然看到 URL 更改會使他們感到不安。

出現這種情況的原因是:在呈現 Web 窗體時,它會將其操作屬性直接設置爲 Request 對象中文件路徑的值。當然,在呈現 Web 窗體時,URL 已從 /Products/Beverages.aspx 重寫爲 ListProductsByCategory.aspx?CategoryID=1,這表明 Request 對象報告用戶要訪問 ListProductsByCategory.aspx?CategoryID=1。只需使服務器端窗體不呈現操作屬性即可解決此問題。(默認情況下, 如果窗體不包含操作屬性,瀏覽器將會回發。)

不幸的是,Web 窗體不允許您明確指定操作屬性,也不允許您設置某些屬性以禁用操作屬性的呈現。因此,我們必須自己來擴展 System.Web.HtmlControls.HtmlForm 類,覆蓋 RenderAttribute() 方法並明確指出它不會呈現操作屬性。

由於繼承功能,我們可以獲得 HtmlForm 類的所有功能,並且只需添加幾行代碼即可獲得所需的行爲。以下顯示了自定義類的完整代碼:

namespace ActionlessForm {
public class Form : System.Web.UI.HtmlControls.HtmlForm
{
protected override void RenderAttributes(HtmlTextWriter writer)
{
writer.WriteAttribute("name", this.Name);
base.Attributes.Remove("name");

writer.WriteAttribute("method", this.Method);
base.Attributes.Remove("method");

this.Attributes.Render(writer);

base.Attributes.Remove("action");

if (base.ID != null)
writer.WriteAttribute("id", base.ClientID);
}
}
}

已被覆蓋的 RenderAttributes() 方法的代碼僅包含 HtmlForm 類的 RenderAttributes() 方法的準確代碼,而不設置操作屬性。(我使用 Lutz Roeder 的 Reflector 來查看 HtmlForm 類的源代碼。)

創建此類並對其進行編譯之後,要在 ASP.NET Web 應用程序中使用它,應首先將其添加到 Web 應用程序的 References 文件夾中。然後,要使用它來代替 HtmlForm 類,只需在 ASP.NET 網頁的頂部添加以下內容即可:

<%@ Register TagPrefix="skm" Namespace="ActionlessForm" 
Assembly="ActionlessForm" %>

然後,將 <form runat="server">(如果有)替換爲:

<skm:Form id="Form1" method="post" runat="server">

並將右邊的 </form> 標記替換爲:

</skm:Form>

您可以在 ListProductsByCategory.aspx(包含在本文的下載代碼中)中發現操作中的此自定義 Web Form 類。下載內容中還包含了用於無操作 Web Form 的 Visual Studio .NET 項目。

注意 : 如果要重寫的目標 URL 沒有執行回發,則無需使用此自定義 Web Form 類。

創建真正“可刪節”的 URL

前一部分中介紹的簡單 URL 重寫顯示瞭如何輕鬆地爲 URL 重寫引擎配置新的重寫規則。但在使用正則表達式時,重寫規則的真正功能纔會發揮更大作用,本部分將對此進行探討。

Blog 在當今正變得越來越流行,似乎每個人都擁有自己的 blog。如果您不熟悉 blog:blog 是經常更新的個人頁面,通常作爲聯機期刊。大多數 blog 只記錄每天發生的事情,還有一些 blog 可能關注於特定的主題(例如,電影回顧、體育團隊或計算機技術)。

可以在任何地點 對 blog 進行更新,更新頻率爲從每天幾次到每週一次或兩次,具體情況取決於作者。通常,blog 主頁將顯示最近的 10 個條目,但實際上,所有 blog 軟件均提供存檔,訪問者可以通過存檔讀取較早的帖子。Blog 是用於“可刪節”URL 的一個功能強大的應用程序。假設在搜索 blog 的存檔時,您在 URL /2004/02/14.aspx 上發現了您自己。如果您發現自己在閱讀 2004 年 2 月 14 日的帖子,您是否覺得很驚訝?而且,您可能希望查看 2004 年 2 月的所有帖子,在這種情況下,您可以嘗試將 URL 刪節爲 /2004/02/。要查看 2004 年的所有帖子,您可以嘗試訪問 /2004/。

維護 blog 時,最好爲訪問者提供此級別的 URL“可刪節性”。許多 blog 引擎都提供此功能,但我們將討論如何使用 URL 重寫來實現此功能。

首先,我們需要一個 ASP.NET 網頁,此頁面將按照日、月或年來顯示 blog 條目。假設我們有一個 ShowBlogContent.aspx 頁面,該頁面的查詢字符串參數爲年、月和日。要查看 2004 年 2 月 14 日的帖子,我們可以訪問 ShowBlogContent.aspx?year=2004&month=2&day=14。要查看 2004 年 2 月的所有帖子,我們可以訪問 ShowBlogContent.aspx?year=2004&month=2。最後,要查看 2004 年的所有帖子,我們可以瀏覽到 ShowBlogContent.aspx?year=2004。(可以在本文的下載內容中找到 ShowBlogContent.aspx 的代碼。)

在這種情況下,如果用戶訪問 /2004/02/14.aspx,我們需要將 URL 重寫爲 ShowBlogContent.aspx?year=2004&month=2&day=14。所有三種情況(URL 指定了年、月和日時;URL 僅指定了年和月時;URL 僅指定了年時)均可使用重寫規則進行處理:

<RewriterConfig>
<Rules>
<!-- Blog 內容顯示程序規則 -->
<RewriterRule>
<LookFor>~/(/d{4})/(/d{2})/(/d{2})/.aspx</LookFor>
<SendTo>~/ShowBlogContent.aspx?year=$1&amp;month=$2&amp;day=$3</SendTo>
</RewriterRule>
<RewriterRule>
<LookFor>~/(/d{4})/(/d{2})/Default/.aspx</LookFor>
<SendTo><![CDATA[~/ShowBlogContent.aspx?year=$1&month=$2]]></SendTo>
</RewriterRule>
<RewriterRule>
<LookFor>~/(/d{4})/Default/.aspx</LookFor>
<SendTo>~/ShowBlogContent.aspx?year=$1</SendTo>
</RewriterRule>
</Rules>
</RewriterConfig>

這些重寫規則表明了正則表達式的功能。在第一個規則中,我們使用模式 (/d{4})/(/d{2})/(/d{2})/.aspx 查找 URL。在簡明英語中,它對應了這樣一個字符串:首先是四個數字,後跟一個斜槓,然後是兩個數字,後跟一個斜槓,然後再跟兩個數字,最後是一個 .aspx。每個數字組周圍的括號非常重要,通過它可以在相應的 <SendTo > 屬性中引用這些括號內的匹配字符。 特別是,我們可以針對第一、第二和第三個括號組分別使用 $1、$2 和 $3 引用回括號內的匹配組。

注意 :由於 Web.config 文件採用 XML 格式,但是必須對元素文字部分中的字符(如 &、< 和 >)進行轉義。在第一個規則的 <SendTo > 元素中,& 被轉義爲 &amp;。在第二個規則的 <SendTo > 中使用了另外一種技術(使用 <![CDATA[...]] > 元素),無需對內部的內容進行轉義。可以使用兩種方法中的任何一種,並且都會得到相同的結果。

圖 5、6 和 7 顯示了操作中的 URL 重寫。數據實際上是從我的 blog http://scottonwriting.net/ 中拖過來的。圖 5 中顯示了 2003 年 11 月 7 日的帖子;圖 6 中顯示了 2003 年 11 月的所有帖子;圖 7 顯示了 2003 年的所有帖子。

5. 2003 年 11 月 7 日的帖子

6. 2003 年 11 月的所有帖子

7. 2003 年的所有帖子

注意 :URL 重寫引擎在 <LookFor > 元素中需要使用正則表達式模式。一個很好的網站:RegExLib.com , 在那裏您可以獲取有關常用正則表達式的幫助信息,還可以共享您自己的自定義正則表達式。

構建必備的目錄結構

當請求 /2004/03/19.aspx 時,IIS 將通知 .aspx 擴展,並將請求路由到 ASP.NET 引擎。請求在 ASP.NET 引擎的管道中移動時,URL 將被重寫爲 ShowBlogContent.aspx?year=2004&month=03&day=19,並且訪問者會看到 2004 年 3 月 19 日的 blog 條目。但是當用戶瀏覽到 /2004/03/ 時將會發生什麼情況呢?除非有一個 /2004/03/ 目錄,否則 IIS 將返回一個 404 錯誤。此外,此目錄中還需要具有 Default.aspx 頁面,以便可以將請求傳遞給 ASP.NET 引擎。

因 此,要使用這種方法,必須手動創建一個用於每年的目錄(其中包含 blog 條目),並且目錄中具有一個 Default.aspx 頁面。另外,在每年目錄中,您需要再手動創建十二個目錄(01、02、?、?...、12),並且每個目錄中均有一個 Default.aspx 文件。(如上所述,我們還必須執行前面演示中的操作,即在 /Products/ 目錄中添加一個 Default.aspx 文件,以便訪問 /Products/ 時可以正確顯示 ListCategories.aspx。)

很顯然,添加這樣一個目錄結構可能是一件很痛苦的事 情。解決此問題的方法是使所有傳入的 IIS 請求都映射到 ASP.NET 引擎。通過這種方法,即使訪問 URL /2004/03/,IIS 也會如實地將請求傳遞給 ASP.NET 引擎(即使並不存在 /2004/03/ 目錄)。但是,使用這種方法將使 ASP.NET 引擎負責處理到達 Web 服務器的所有類型的傳入請求,包括圖像、CSS 文件、外部 JavaScript 文件、Macromedia Flash 文件,等等。

對處理所有文件類型的全面討論遠遠超出了本文的範圍。有關使用此技術的 ASP.NET Web 應用程序的示例,請參閱 .Text,一個開放源 blog 引擎。.Text 可以配置爲將所有請求均映射到 ASP.NET 引擎。它可以使用自定義 HTTP 處理程序來處理生成所有文件類型的問題,自定義 HTTP 處理程序瞭解如何生成典型的靜態文件類型(圖像、CSS 文件,等等)。

結論

在本文中,我們討 論瞭如何在 ASP.NET 級別通過 HttpContext 類的 RewriteUrl() 方法來執行 URL 重寫。正如我們所看到的,RewriteUrl() 更新了特定的 HttpContext's Request 屬性,從而更新了被請求的文件和路徑。最終結果是,從用戶角度來看,他們要訪問某個特定的 URL,但從 Web 服務器端來看,被請求的卻是另一個 URL。

可以在 HTTP 模塊或 HTTP 處理程序中重寫 URL。在本文中,我們介紹瞭如何使用 HTTP 模塊執行重寫,並討論了在管道中的不同階段執行重寫的結果。

當然,如果執行 ASP.NET 級別的重寫,則僅當已成功地將請求從 IIS 傳遞給 ASP.NET 引擎後纔會發生 URL 重寫。實際上,只有用戶請求帶 .aspx 擴展名的頁面時纔會出現這種情況。但是,如果您要使用戶可以進入實際並不存在的 URL,但又希望重寫到現有的 ASP.NET 頁面,則必須創建虛擬目錄和 Default.aspx 頁面,或者對 IIS 進行配置,以使所有傳入請求一律被路由到 ASP.NET 引擎。

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