日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

在ASP.NET中实现Url Rewriting

發(fā)布時(shí)間:2025/4/14 asp.net 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在ASP.NET中实现Url Rewriting 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
概要

  分析如何使用微軟提供的ASP.NET來對(duì)動(dòng)態(tài)產(chǎn)生的URL地址進(jìn)行網(wǎng)址重寫。 網(wǎng)址重寫是實(shí)現(xiàn)一種截取網(wǎng)址請(qǐng)求并將其進(jìn)行處理后重新指向到一個(gè)指定的網(wǎng)址的過程。作者本人在對(duì)各種實(shí)現(xiàn)網(wǎng)址重寫的技術(shù)進(jìn)行研究和探討后得出的經(jīng)驗(yàn)和方法,希望能對(duì)您有所幫助。

  內(nèi)容簡介

   稍微花點(diǎn)時(shí)間看一看你做的網(wǎng)站里頭的URL地址,你看到類似這樣的地址嗎http://yoursite.com/info /dispEmployeeInfo.aspx?EmpID=459-099&type=summary ?也許你會(huì)出于某種目的把大量的頁面文件從一個(gè)目錄甚至一個(gè)網(wǎng)站轉(zhuǎn)移到其他地方,而許多訪問者出于個(gè)人興趣或者研究目的之前就已經(jīng)將原有網(wǎng)址收藏了起來, 如果這時(shí)他從收藏夾打開該頁面的時(shí)候發(fā)現(xiàn)這已經(jīng)是壞鏈了。本文旨在介紹如何使用網(wǎng)址重寫將那些“難看”的網(wǎng)址轉(zhuǎn)換成比較有實(shí)際意義的網(wǎng)址,使其便于記憶。 例如將http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099& type=summary轉(zhuǎn)換成如下地址:http://yoursite.com/ dispEmployeeInfo/459-099/summary.html 。我們甚至發(fā)現(xiàn) 網(wǎng)址重寫技術(shù)可以解決令人頭疼的404錯(cuò)誤,或者說它可以創(chuàng)建一個(gè)智能化的404錯(cuò)誤解決方案。

  如上所述,網(wǎng)址重寫是實(shí)現(xiàn)一種截取網(wǎng) 址請(qǐng)求并將其進(jìn)行處理后重新指向到一個(gè)指定的網(wǎng)址的過程。 在網(wǎng)址重寫執(zhí)行的期間,相應(yīng)處理程序處理被請(qǐng)求的網(wǎng)址,從中提取出相關(guān)的值,然后重新指向一個(gè)新的指定地址。例如:由于一次網(wǎng)站目錄調(diào)整,原有的 /people/ 子目錄下的所有網(wǎng)頁全部移動(dòng)到/info/employees/目錄,原訪問者從收藏夾或者其他什么地方點(diǎn)擊鏈接發(fā)出訪問/people/目錄下的文件的 請(qǐng)求時(shí),你肯定希望他還是能通過原有地址看到和原來相同的頁面,但實(shí)際上看到的卻是網(wǎng)址重寫指向的新目錄下的相應(yīng)文件。

  在老版本 ASP中,使用網(wǎng)址重寫技術(shù)的途徑很少,要么寫一個(gè) ISAPI過濾器,要么購買第三方廠商提供的網(wǎng)址重寫組件,然而在微軟提供的ASP.NET下你可以通過多種方法很簡單地開發(fā)出自己的網(wǎng)址重寫軟件,以滿 足自己各種不同的需要。本文將和你一起討論這門針對(duì)ASP.NET開發(fā)人員的實(shí)現(xiàn)網(wǎng)址重寫的技術(shù),然后舉一些網(wǎng)址重寫實(shí)際應(yīng)用的例子。在我們深入探討網(wǎng)址 重寫技術(shù)的細(xì)節(jié)之前,我們先看一下日常使用網(wǎng)址重寫技術(shù)實(shí)現(xiàn)的場(chǎng)景。

  網(wǎng)址重寫的一般用途

   創(chuàng)建一個(gè)數(shù)據(jù)操作的ASP.NET程序最常見的就是一個(gè)aspx頁面后面帶上一些查詢參數(shù)集合。例如在設(shè)計(jì)一個(gè)電子商務(wù)網(wǎng)站的時(shí)候,假定你設(shè)計(jì)了一項(xiàng)功 能允許用戶瀏覽待售的商品,為了更加方便操作,你設(shè)計(jì)了一個(gè)頁面displayCategory.aspx將商品按照給定的分類顯示,那么該分類下的商品 顯示頁面上應(yīng)該在頁面文件對(duì)應(yīng)網(wǎng)址后面加上了一個(gè)商品分類的查詢參數(shù),例如用戶要查詢待售的“裝飾品”,在數(shù)據(jù)庫中所有的裝飾品數(shù)據(jù)對(duì)應(yīng)的分類編號(hào) CategoryID的值為5,那么用戶會(huì)訪問如下網(wǎng)址:http://yoursite.com /displayCategory.aspx?CategoryID=5。

  創(chuàng)建一個(gè)包含類似這樣網(wǎng)址的網(wǎng)站最終有兩種結(jié)果,首先從最 終用戶的角度來觀察,http://yoursite.com/displayCategory.aspx?CategoryID=5 這個(gè)網(wǎng)址有些雜亂, 可行性分析專家Jakob Neilson(主頁: http://useit.com/) 建議選擇網(wǎng)址顯示方式時(shí)候考慮如下要求(參考網(wǎng)址:http://www.useit.com/alertbox/990321.html):

  · 是否簡短

  · 是否易于輸入

  · 是否將站點(diǎn)結(jié)構(gòu)形象化

  · 是否具有隱蔽性,也就是讓用戶通過一個(gè)虛擬的看似有意義的導(dǎo)航地址訪問指向該地址

   我想還應(yīng)該在上述列表中再增加一條: 是否便于記憶。http://yoursite.com/displayCategory.aspx?CategoryID=5 這個(gè)地址沒有一個(gè)地方符合Neilson標(biāo)準(zhǔn)的任何一條,也不便于記憶。當(dāng)然,對(duì)于有經(jīng)驗(yàn)的網(wǎng)絡(luò)開發(fā)專家來說,他們很熟悉這種鍵值對(duì)構(gòu)成的查詢參數(shù)結(jié)構(gòu)體 系,然而對(duì)于普通用戶來說輸入這些帶有參數(shù)的網(wǎng)址實(shí)在是太麻煩了。

  一種較好的方法就是使用一種比較直觀且容易記憶的方式來將網(wǎng)址表示 為: http://yoursite.com/products/Widgets 乍一看很容易就會(huì)推斷這個(gè)網(wǎng)址所對(duì)應(yīng)的內(nèi)容極有可能會(huì)是顯示裝飾品(Widgets)信息,這個(gè)網(wǎng)址就變得更加容易記憶和傳播!然后我告訴我的同事:“請(qǐng) 查看這個(gè)網(wǎng)址:http://yoursite.com/products/widgets ”不用我說第二遍,她可能一次就把地址敲到瀏覽器上了(你也可以在亞馬遜(Amazon.com)的網(wǎng)站上這樣嘗試一下)。很快就瀏覽器上就列出了裝飾品 (Widgets)的內(nèi)容。這里“隱蔽性”表示:用戶可以自行變更網(wǎng)址的結(jié)尾,例如輸入:http://yoursite.com/products 就能看到全部分類相關(guān)的商品列表或者列出所有相關(guān)商品分類目錄列表。

  注:用上述簡單的變更網(wǎng)址內(nèi)容的方法來構(gòu)思一下如今的比較流行的 Blog網(wǎng)站生成的網(wǎng)址。例如:要查詢2004年1月28日所發(fā)的帖子,只需輸入 http://someblog.com/2004/01/28 即可,如果將網(wǎng)址裁減為 http://someblog.com/2004/01 則顯示 2004年1月份的帖子 ,同樣將月份裁減掉得到 http://someblog.com/2004 則顯示出2004年全年所發(fā)的帖子。

  網(wǎng)址重寫技術(shù)除了用于將復(fù)雜的網(wǎng)址簡單化之外,它還能用于處理因網(wǎng)站目錄調(diào)整或者其他原因?qū)е庐a(chǎn)生大量的無效鏈接和過期書簽。

  當(dāng)一個(gè)Web請(qǐng)求傳送到IIS會(huì)發(fā)生什么?

   在探討如何實(shí)現(xiàn)網(wǎng)址重寫這項(xiàng)技術(shù)之前,很有必要了解一下IIS是處理所接收的Web請(qǐng)求的機(jī)制。 當(dāng)一個(gè)Web請(qǐng)求到達(dá)IIS Web服務(wù)器時(shí),IIS會(huì)根據(jù)所請(qǐng)求的文件后綴名來決定如何處理該請(qǐng)求,IIS可以處理諸如HTML頁面、圖片、靜態(tài)內(nèi)容,或者將請(qǐng)求轉(zhuǎn)發(fā)給ISAPI應(yīng) 用程序,由該ISAPI應(yīng)用程序處理后生成HTML靜態(tài)內(nèi)容返回給IIS,最后由IIS將請(qǐng)求結(jié)果發(fā)送回給客戶端。(一個(gè)ISAPI應(yīng)用程序就是一套編譯 好能隨時(shí)在后臺(tái)運(yùn)行的類庫,它的任務(wù)就是根據(jù)請(qǐng)求生成相關(guān)的內(nèi)容。)

  例如:如果IIS接收到一個(gè)對(duì)Info.asp的請(qǐng)求,它會(huì)將該 請(qǐng)求轉(zhuǎn)交給 asp.dll來處理,該ISAPI應(yīng)用程序調(diào)出并執(zhí)行所請(qǐng)求的ASP頁面,然后把生成的HTML代碼返回給IIS,IIS最后把內(nèi)容發(fā)送回請(qǐng)求客戶端。 對(duì)于ASP.NET頁面,IIS則將請(qǐng)求轉(zhuǎn)交給名為 aspnet_isapi.dll的ISAPI應(yīng)用程序來處理,該ISAPI應(yīng)用程序 調(diào)用托管的ASP.NET工作進(jìn)程來處理該請(qǐng)求,并將生成的HTML代碼返回給請(qǐng)求客戶端。

  你可以自定義IIS, 將某一類擴(kuò)展名映射到指定的ISAPI應(yīng)用程序,圖一顯示了IIS管理工具中的應(yīng)用程序配置對(duì)話框。


圖一.已配置的文件擴(kuò)展名映射

  關(guān)于對(duì)IIS如何管理所接收的請(qǐng)求的詳細(xì)探討有些超出本文內(nèi)容,,重要的是要了解 ASP.NET引擎只負(fù)責(zé)處理對(duì)擴(kuò)展名已經(jīng)被正確配置映射到aspnet_isapi.dll的網(wǎng)絡(luò)請(qǐng)求。用ISAPI過濾器來分析請(qǐng)求

  除了將請(qǐng)求的文件擴(kuò)展名映射到相應(yīng)的ISAPI應(yīng)用程序外,IIS 還執(zhí)行一些其他工作。例如 IIS還主動(dòng)對(duì)發(fā)出請(qǐng)求的客戶端用戶進(jìn)行授權(quán),并判斷已授權(quán)用戶是否對(duì)其請(qǐng)求的文件擁有訪問權(quán)限,在一個(gè)請(qǐng)求過程的全部生命期內(nèi),IIS的處理經(jīng)歷了幾個(gè) 階段,在每一個(gè)階段IIS都生成一個(gè)事件,而該事件可以被ISAPI過濾器實(shí)時(shí)操控的。

  如同ISAPI應(yīng)用程序一樣, ISAPI過濾器也是一塊塊安裝在Web服務(wù)器上的非托管代碼。 ISAPI應(yīng)用程序用于對(duì)所接收的特定文件類型做出響應(yīng),而ISAPI過濾器含有對(duì)IIS生成的事件做出響應(yīng)的代碼(contain Code),甚至可以編輯進(jìn)出的數(shù)據(jù)。ISAPI也含有眾多應(yīng)用程序,包括:

  · 權(quán)限控制與授權(quán)(Authentication and Authorization)

  · 日志記錄與監(jiān)視(Logging and Monitoring)

  · HTTP內(nèi)容壓縮(HTTP Compression)

  · 網(wǎng)址重寫(URL Rewriting)

  本文所探討的用ASP.NET實(shí)現(xiàn)的網(wǎng)址重寫技術(shù)就是 基于ISAPI過濾器用于網(wǎng)址重寫的技術(shù)內(nèi)容,然而我們?nèi)匀灰懻撘幌戮烤故鞘褂肐SAPI過濾器還是使用ASP.NET應(yīng)用程序提供的技術(shù)來實(shí)現(xiàn)網(wǎng)址重寫技術(shù)。

  當(dāng)一個(gè)請(qǐng)求傳入ASP.NET引擎的時(shí)候會(huì)發(fā)生什么?

   ASP.NET問世之前,在IIS Web服務(wù)器上的網(wǎng)址重寫功能需要通過ISAPI過濾器來實(shí)現(xiàn),自從這個(gè)家伙問世后我們就能通過ASP.NET來實(shí)現(xiàn)URL重寫了,因?yàn)锳SP.NET的 解釋引擎與IIS有極大的相似之處,產(chǎn)生這些相似性主要是因?yàn)?ASP.NET:

  · 在處理接收的請(qǐng)求的生命期內(nèi)也會(huì)產(chǎn)生事件;

  · 允許任意數(shù)量的HttpModule操控產(chǎn)生的事件,這與IIS中的ISAPI過濾器類似;

  · 將請(qǐng)求的資源委托給HttpHandler處理,這與IIS中的ISAPI應(yīng)用程序類似。

  和IIS一樣,在一個(gè)請(qǐng)求的整個(gè)生命期內(nèi),ASP.NET對(duì)該請(qǐng)求的處理狀態(tài)發(fā)出的狀態(tài)改變信號(hào)引發(fā)相應(yīng)的事件。例如:BeginRequest事件在ASP.NET開始響應(yīng)客戶端請(qǐng)求之始引發(fā);AuthenticateRequest事件在ASP.NET確立用戶身份后引發(fā),當(dāng)然還有諸如AuthorizeRequestResolveRequestCacheEndRequest等其它很多事件,這些 都是System.Web.HttpApplication類下的事件,更多信息請(qǐng)參考技術(shù)文檔中的類HttpApplication概要。

  如上所述, 可以創(chuàng)建ISAPI過濾器并用于相應(yīng)IIS引發(fā)的事件,同理,ASP.NET也提供了HttpModule用于響應(yīng)ASP.NET引擎引發(fā)的事件,一個(gè)ASP.NET應(yīng)用程序 通過配置可以擁有多個(gè)HttpModule。ASP.NET引擎 每處理一個(gè)請(qǐng)求,便初始化一個(gè)相應(yīng)配置好的HttpModule,并允許它 針對(duì)請(qǐng)求處理期間引發(fā)的事件生成相應(yīng)的事件委托。事實(shí)上ASP.NET引擎 處理每一個(gè)請(qǐng)求調(diào)用大量的事件委托。FormsAuthenticationModule就是眾多內(nèi)嵌HttpModule中的一個(gè),它 首先檢查是否使用表單授權(quán),如果是的話,它 將檢查用戶是否已授權(quán),如果沒有授權(quán)則自動(dòng)把用戶重定向到指定的登錄頁面。(即:在asp.net中可以直接記錄并判別用戶登錄授權(quán)的問題了!)

   回憶在IIS中,一項(xiàng)請(qǐng)求最后被轉(zhuǎn)交給一個(gè)ISAPI應(yīng)用程序處理,該應(yīng)用程序針對(duì)每一項(xiàng)請(qǐng)求進(jìn)行處理并返回相應(yīng)的數(shù)據(jù)。例如,客戶端發(fā)出一個(gè)訪問經(jīng)典 ASP頁面的請(qǐng)求,IIS將該請(qǐng)求轉(zhuǎn)交給asp.dll程序處理,asp.dll針對(duì)該請(qǐng)求執(zhí)行asp頁面內(nèi)容,并返回HTML編碼。ASP.NET也使 用了類似的手法,ASP.NET引擎在將這些HttpModule初始化后,判斷并決定調(diào)用相應(yīng)的HttpModule來處理該請(qǐng)求。(問:怎么程序操作httpModule)

  所有通過ASP.NET引擎解析的請(qǐng)求最終被送交一個(gè)HttpHandler或者HttpHandlerFactory(一個(gè)HttpHandler只是簡單地返回一個(gè)用于處理該請(qǐng)求的HttpHandler的實(shí)例。)最終的委托呈現(xiàn)并響應(yīng)所請(qǐng)求的HTML編碼,并發(fā)送回IIS,IIS則將HTML返回給請(qǐng)求客戶端。
ASP.NET包含許多HttpHandler,例如,PageHandlerFactory是用于呈現(xiàn)ASP.NET頁面內(nèi)容,WebServiceHandlerFactory用于呈現(xiàn)ASP.NET Web服務(wù)的SOAP數(shù)據(jù)包,TraceHandler用于將ASP.NET請(qǐng)求資源的HTML標(biāo)記寫入trace.axd。

  圖二描繪了一個(gè)針對(duì)ASP.NET資源的請(qǐng)求所經(jīng)過的處理流程。首先,IIS接收到該請(qǐng)求并將其轉(zhuǎn)交給aspnet_isapi.dll。其次,ASP.NET引擎將一些HttpModule初始化。最后,最終的HttpHandler被調(diào)用,生成相應(yīng)的標(biāo)記語言,并將其返回給IIS,最終返回到請(qǐng)求客戶端。


圖二.IIS和ASP.NET對(duì)請(qǐng)求的處理過程

  創(chuàng)建并注冊(cè)自定義HttpModule和HttpHandler

  創(chuàng)建自定義HttpModule的工作相對(duì)較簡單,它包括一個(gè)實(shí)現(xiàn)當(dāng)前接口的托管類,HttpModule必須實(shí)現(xiàn)System.Web.IHttpModule接口,同樣HttpHandlerHttpHandlerFactory必須分別實(shí)現(xiàn)System.Web.IHttpHandler接口和System.Web.IhttpHandlerFactory接口。有關(guān)創(chuàng)建HttpHandlerHttpModule的細(xì)節(jié)已經(jīng)超出本書范圍。

  一旦HttpModuleHttpHandler被創(chuàng)建后,必須向Web應(yīng)用程序注冊(cè)。如果要 向整個(gè)Web服務(wù)器HttpModuleHttpHandler只需簡單的寫入machine.config文件;如果是 由指定的Web應(yīng)用程序調(diào)用則需在該程序的web.config配置文件中添加幾行XML標(biāo)記。

  例如,要向指定的Web應(yīng)用程序注冊(cè)HttpModuleHttpHandler,只需向該Web應(yīng)程序的web.config配置文件中configuration\System.Web節(jié)中添加下列幾行:



<HttpModules>
<add type="type" name="name" />
</HttpModules>

  其中type屬性為HttpModule的標(biāo)識(shí)號(hào)和類庫名稱,name屬性則為該模塊取一個(gè)較為友好的名稱方便 在Global.asax調(diào)用。
HttpHandlerHttpHandlerFactory則是 在web.config文件中configuration\System.Web節(jié)中添加<httpHandler>標(biāo)記,例如:

<httpHandlers>
<add verb="verb" path="path" type="type" />
</HttpModules>

   回憶上文, ASP.NET對(duì)每一個(gè)接收到的請(qǐng)求指派相應(yīng)的HttpHandler來處理并呈現(xiàn)相應(yīng)內(nèi)容,該指派決定于所接收請(qǐng)求的verb和path的內(nèi) 容,verb為HTTP請(qǐng)求的類型:GET或者POST,path則為請(qǐng)求的文件的路徑和文件名。如果我們打算用一個(gè)HttpHandler來處理所有GET類型和POST類型的并且文件擴(kuò)展名為.scott的內(nèi)容,可以在web.config相應(yīng)配置節(jié)中加入下列標(biāo)記:

<httpHandlers>
<add varb="*" path=".scott" type="type" />
</httpHandlers>

  其中 type是我們定義的HttpHandler的類型。

  注意:在注冊(cè)HttpHandler的時(shí)候必須注意HttpHandler所 使用的文件擴(kuò)展名必須已經(jīng)在IIS中做指向ASP.NET引擎的映射,在上面.scott擴(kuò)展名的例子中,如果我們所使用的.scott擴(kuò)展名如果沒有在 IIS中做指向ASP.NET引擎的映射的話,假定對(duì)foo.scott文件發(fā)出請(qǐng)求,該請(qǐng)求 將導(dǎo)致IIS將foo.scott文件內(nèi)容直接呈現(xiàn)給客戶端,為了能夠讓HttpHandler處理該請(qǐng)求,必須將.scott擴(kuò)展名在IIS中做指向ASP.NET引擎的映射,之后IIS才能正確地將.scott的請(qǐng)求轉(zhuǎn)交給相應(yīng)的HttpHandler

實(shí)現(xiàn)網(wǎng)址重寫

  網(wǎng)址重寫技術(shù)不但可以在IIS Web服務(wù)器一級(jí)通過ISAPI過濾器實(shí)現(xiàn),而且還可以在ASP.NET一級(jí)通過HttpModule或者HttpHandler實(shí)現(xiàn)。本文主要關(guān)注在ASP.NET一級(jí)實(shí)現(xiàn)網(wǎng)址重寫技術(shù),所以此時(shí)不必關(guān)注在ISAPI應(yīng)用程序中實(shí)現(xiàn)網(wǎng)址重寫的技術(shù)細(xì)節(jié),而且有很多第三方廠商提供的ISAPI過濾器。

  構(gòu)建網(wǎng)址重寫引擎

  在ASP.NET中實(shí)現(xiàn)網(wǎng)址重寫很簡單, 只需調(diào)用System.Web.HttpContext類的RewritePath()方法即可。HttpContext類中包含有關(guān)于特定HTTP請(qǐng)求的HTTP規(guī)范信息。ASP.NET引擎每接收到一個(gè)特定請(qǐng)求后便針對(duì)該請(qǐng)求創(chuàng)建一個(gè)特定的實(shí)例,這個(gè)類包含一些屬性諸如: RequestResponse屬性,分別提供對(duì)請(qǐng)求和響應(yīng)的訪問;ApplicationSession屬性提供對(duì)Application變量和Session變量的訪問;User屬性提供對(duì)已授權(quán)用戶信息的訪問。

  在微軟.NET Framework 1.0版本中,RewritePath()方法接收一個(gè)新路徑的簡單字符串,在其內(nèi)部HttpContext類的RewritePath(string)方法內(nèi)在地更新Request對(duì)象的路徑和查詢參數(shù)。除了RewritePath(string)方法之外,.NET Framework 1.1版還提供了另外一些重載版本,其中一個(gè)重載版本接收三個(gè)輸入字符串參數(shù),這種交替的重載形式不僅僅只是設(shè)置Request對(duì)象的路徑和查詢參數(shù)這些屬性,而是設(shè)置更深層的成員變量,這些成員變量用于為 PhysicalPathPathInfoFilePath屬性計(jì)算Request對(duì)象值。

  為了實(shí)現(xiàn)ASP.NET中的網(wǎng)址重寫,我們需要?jiǎng)?chuàng)建一個(gè)HttpHandlerHttpModule用于:

  ·根據(jù)請(qǐng)求的路徑?jīng)Q定所需要重寫的路徑;

  ·重寫路徑,如果需要的話可以調(diào)用RewritePath方法;

   以前文所構(gòu)建的那個(gè)站點(diǎn)為例,可以通過/info/employee.aspx?empID=EmployeeID來訪問每一個(gè)雇員的信息。為了使這個(gè) 網(wǎng)址更加地具有“隱蔽性”,我們可能會(huì)使用更加容易理解的訪問方式如:/people/雇員名.aspx。這里就有了一個(gè)網(wǎng)址重寫的案例:當(dāng)接收到對(duì) /people/ScottMitchell.aspx的請(qǐng)求的時(shí)候,我們就得使用網(wǎng)址重寫使得對(duì)該頁面的請(qǐng)求被重寫指向到先前使用的/info /employee?EmpID=1001地址。

  使用HttpModule來調(diào)用網(wǎng)址重寫

  在ASP.NET一級(jí)來執(zhí)行網(wǎng)址重寫,既可以使用HttpHandler,也可以使用HttpModule。當(dāng)使用HttpModule的時(shí)候,必須決定如果該網(wǎng)址需要被重寫的話,究竟應(yīng)該在整個(gè)請(qǐng)求的生命周期期間的那一個(gè)點(diǎn)來使用。乍一看著有些武斷,但是這個(gè)決定以重大而且微妙的方式影響到你的應(yīng)用程序。之所以作出對(duì)網(wǎng)址重寫點(diǎn)的選擇是因?yàn)閮?nèi)嵌的ASP.NET HttpModule使用Request對(duì)象的屬性值來完成自己的工作(回憶一下重寫路徑對(duì)Request對(duì)象的屬性值的改變),這些內(nèi)嵌HttpModule和相應(yīng)事件的密切關(guān)系列舉如下:

HttpModule事件簡介
FormsAuthenticationModuleAuthenticateRequest判斷用戶是否已通過表單授權(quán)方式獲取授權(quán),如果沒有的話則將用戶重定向到指定的登錄頁面
FileAuthorizationModuleAuthorizeRequest當(dāng)使用Windows授權(quán)方式的時(shí)候,該HttpModule判斷并確定該Microsoft Windows帳戶是否對(duì)其請(qǐng)求的資源擁有足夠的權(quán)限
UrlAuthorizationModuleAuthorizeRequest檢查并確認(rèn)請(qǐng)求者是否對(duì)所訪問的網(wǎng)址擁有權(quán)限。該Url授權(quán)可以在web.config文件的<authorization>和<location>元素中配置

  回想一下BeginRequest事件在AuthenticateRequest事件之前引發(fā),而AuthenticateRequest事件又在AuthorizeRequest事件之前引發(fā)。

  實(shí)現(xiàn)網(wǎng)址重寫的一個(gè)較為安全的場(chǎng)合就是把它放在在BeginRequest事件中執(zhí)行,這意味著如果要執(zhí)行網(wǎng)址重寫的話,在眾多內(nèi)嵌HttpModule運(yùn)行的時(shí)候他已經(jīng)完成了。這種途徑的最終用途淋漓盡致地體現(xiàn)在表單驗(yàn)證上。當(dāng)用戶訪問受限資源的時(shí)候, 如果之前使用了表單驗(yàn)證,他會(huì)自動(dòng)被重定向到指定的登錄頁面,在成功登錄之后,用戶被重定向回先前試圖訪問的受限制頁面。

  如果把網(wǎng)址重寫放在BeginRequest事件或者AuthenticateRequest事 件中,在登錄頁面上執(zhí)行提交后,該頁面會(huì)將用戶重定向到網(wǎng)址重寫指定的頁面。假定當(dāng)用戶在瀏覽器上敲入/people /ScottMitchell.aspx地址,該地址是要被重定向到/info/employee.aspx?EmpID=1001的, 如果該Web應(yīng)用程序設(shè)定使用表單驗(yàn)證,當(dāng)用戶開始訪問/people/ScottMitchell.aspx的時(shí)候,該網(wǎng)址將重寫指向/info /employee.aspx?EmpID=1001,接著ForumAuthenticationModule啟動(dòng),如果需要的話將用戶重定向到登錄頁面,用戶登錄后重定向到的頁面將是/info/employee.aspx?EmpID=1001,這也是自從FormAuthenticationModule啟動(dòng)運(yùn)行時(shí)所發(fā)出請(qǐng)求的頁面。
同上類似,當(dāng)把網(wǎng)址重寫放在BeginRequest事件或者AuthenticateRequest事件中運(yùn)行的時(shí)候,UrlAuthenticationModule也發(fā)現(xiàn)了網(wǎng)址重寫指向的網(wǎng)址,這意味著如果在該應(yīng)用程序的web.config文件中<location>節(jié)為特定的網(wǎng)址配置特定的授權(quán)地址的話,你得引用重寫所指向的網(wǎng)址。

  為了解決這個(gè)微妙的問題,一個(gè)可能就是把網(wǎng)址重寫放在AuthorizeRequest事件中運(yùn)行,但是在使用這種方法解決URL授權(quán)和表單授權(quán)的異常時(shí)又引入了一個(gè)新的缺陷:文件授權(quán)會(huì)失效。當(dāng)使用Windows驗(yàn)證的時(shí)候,FileAuthorizationModule檢查并驗(yàn)證已通過驗(yàn)證的用戶是否擁有足夠的權(quán)限訪問特定的ASP.NET頁面。

   假定有一群用戶并沒有Windows級(jí)別的訪問權(quán)限訪問C:\inetpub\wwwroot\info\employee.aspx,當(dāng)這些用戶試圖 訪問/info/employee.aspx?EmpID=1001的時(shí)候,他們會(huì)得到未授權(quán)的錯(cuò)誤,如果我們把網(wǎng)址重寫放到AuthenticateRequest事件中運(yùn)行,當(dāng)FileAuthorizationModule驗(yàn)證該安全性設(shè)置的時(shí)候,他仍任人為被請(qǐng)求的文件是/people/ScottMitchell.aspx,而這時(shí)該網(wǎng)址已經(jīng)被重寫了,因此FileAuthorizationModule會(huì)直接放行,讓用戶看到了網(wǎng)址重寫指向的內(nèi)容:/info/employee.aspx?Empid=1001。

  那么什么時(shí)候在HttpModule調(diào)用網(wǎng)址重寫合適呢?他決定于所使用的驗(yàn)證方式,當(dāng)然如果不使用驗(yàn)證方式的話,那么無論是在BeginRequest事件、AuthenticateRequest事件還是AuthorizeRequest事件中調(diào)用網(wǎng)址重寫沒有多大區(qū)別,如果使用表單驗(yàn)證方式并且不使用Windows驗(yàn)證方式的話,把網(wǎng)址重寫放入AuthorizeRequest事件委托中調(diào)用既可,如果使用Windows驗(yàn)證方式的話,把這項(xiàng)功能放入BeginRequest事件或者AuthenticateRequest事件調(diào)用就行了。?使用HttpHandler來調(diào)用網(wǎng)址重寫

  除了上面所述方法外,網(wǎng)址重寫也可以放入HttpHandler或者HttpHandlerFactory中調(diào)用。HttpHandler是一個(gè)負(fù)責(zé)針對(duì)特定請(qǐng)求生成相應(yīng)內(nèi)容的類,而HttpHandlerFactory返回一個(gè)HTTP的實(shí)例,該實(shí)例針對(duì)特定請(qǐng)求生成相應(yīng)內(nèi)容。

  本節(jié)將著眼于為這些ASP.NET頁面創(chuàng)建一個(gè)網(wǎng)址重寫的HttpHandlerFactory。創(chuàng)建HttpHandlerFactory必須實(shí)現(xiàn)IHTTPHandlerFactory接口,它包括一個(gè)GetHandler()方法。ASP.NET引擎在初始化這些HttpModule后做出決定針對(duì)該請(qǐng)求調(diào)用相應(yīng)的HttpHandler或者HttpHandlerFactory,在調(diào)用HttpHandlerFactory的時(shí)候,針對(duì)該Web請(qǐng)求以及隨同的其他信息的HttpContext中經(jīng)過的的HttpHandlerFactoryGetHandler()方法將被ASP.NET引擎調(diào)用,HttpHandlerFactory必須返回一個(gè)能委托該請(qǐng)求的對(duì)象,并且該對(duì)象要能實(shí)現(xiàn)IHttpHandler接口。

  要通過一個(gè)HttpHandler來調(diào)用網(wǎng)址重寫,可以先創(chuàng)建一個(gè)HttpHandlerFactory,它的GetHandler()方法檢查所請(qǐng)求的網(wǎng)址并決定是否需要調(diào)用網(wǎng)址重寫。如果要調(diào)用網(wǎng)址重寫的話則調(diào)用前文所述的已通過檢查的HttpContext對(duì)象的RewritePath()方法。最后該HttpHandlerFactory返回一個(gè)由類System.Web.UI.PageParserGetCompiledInstance()方法返回的HttpHandler。(這與內(nèi)嵌于ASP.NET頁面的HttpHandlerFactoryPageHandlerFactory)的工作原理相同。)

  在所有HttpModule被初始化后,HttpHandlerFactory就開始被實(shí)例化。把網(wǎng)址重寫放在這些事件場(chǎng)所的最后一個(gè)里頭調(diào)用的時(shí)候,也會(huì)碰到相同的問題:文件授權(quán)將會(huì)失效。如果非要依賴于Windows驗(yàn)證和文件驗(yàn)證的時(shí)候,你可能得使用HttpModule來調(diào)用網(wǎng)址重寫了。
下一章我們著眼于如何構(gòu)建一個(gè)可重用的網(wǎng)址重寫引擎,使用下文所提的這些示例均以真實(shí)案例作為參照,在作者主頁上提供下載。先用用一個(gè)簡單的網(wǎng)址重寫的例子來探討如何實(shí)現(xiàn)網(wǎng)址重寫,緊接著將利用網(wǎng)址重寫引擎中正則表達(dá)式的強(qiáng)大處理能力來展示真正“隱蔽”的網(wǎng)址重寫技術(shù)!

  使用網(wǎng)址重寫引擎實(shí)現(xiàn)簡單的網(wǎng)址重寫

  為了便于在Web應(yīng)用程序中實(shí)現(xiàn)網(wǎng)址重寫,我構(gòu)建了一個(gè)網(wǎng)址重寫引擎,該引擎提供下列功能:

  ·可以在web.config文件中為頁面開發(fā)者定義其所使用的網(wǎng)址重寫引擎的規(guī)則;

  ·通過使用正則表達(dá)式來使所制定的網(wǎng)址重寫規(guī)則具有更加強(qiáng)大的重寫能力;

  ·能夠通過簡單配置即可在HttpModuleHttpHandler中使用網(wǎng)址重寫。

  本節(jié)只探討通過HttpModule來實(shí)現(xiàn)網(wǎng)址重寫,要了解如何通過HttpHandler來實(shí)現(xiàn)網(wǎng)址重寫請(qǐng)下載本文提供的代碼。

  1. 設(shè)置網(wǎng)址重寫引擎的配置信息

  我們來探討一下在web.config中網(wǎng)址重寫規(guī)則的配置節(jié)。首先必須在web.config文件中指出是否需要在HttpHandler或者HttpModule中調(diào)用網(wǎng)址重寫,在web.config中,下文已經(jīng)包含了兩個(gè)已經(jīng)被注釋掉的配置節(jié):

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

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

  被注釋掉的<HttpModules>為配置使用HttpModule調(diào)用網(wǎng)址重寫;注釋掉的<httpHandler>為配置使用HttpHandler調(diào)用網(wǎng)址重寫。
不論配置使用<HttpModules>還是<httpHandlers>調(diào)用網(wǎng)址重寫,除此之外還須配置網(wǎng)址重寫規(guī)則,一條重寫規(guī)則包括兩項(xiàng)字符串:請(qǐng)求URL中的查找模式和針對(duì)該模式的匹配成功后的替換字符串。該信息在web.config文件中用下列標(biāo)簽描述:

<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>

  每一條規(guī)則都用一個(gè)<RewriterRule>元素表示,以<LookFor>節(jié)表示查詢模式,當(dāng)查詢模式發(fā)現(xiàn)匹配字符串時(shí)便用<SendTo>節(jié)表示的字符串進(jìn)行替換。這些規(guī)則從上到下進(jìn)行查詢匹配,如果找到一個(gè)匹配則按此規(guī)則執(zhí)行網(wǎng)址重寫,并且停止查找。

   配置<LookFor>節(jié)要使用正則表達(dá)式來進(jìn)行字符串匹配和替換。(在此我們舉一個(gè)例子來說明如何使用正則表達(dá)式來對(duì)字符串進(jìn)行匹配和替換。)既然該 查找模式是一個(gè)正則表達(dá)式,那么要注意避開對(duì)正則表達(dá)式保留字符串的直接使用。(正則表達(dá)式的保留字符串包括有:.,?,^,$,等等,可以通過在前面加 上一個(gè)反斜線來引用這些保留字符,例如\.表示引用一個(gè)句點(diǎn))

  2. 使用HttpModule來執(zhí)行網(wǎng)址重寫

  創(chuàng)建一個(gè)HttpModule很簡單,只要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)IHttpModule接口的類,該IHttpModule接口定義了兩個(gè)方法:

  ·Init(HttpApplication),該方法在HttpModule初始化時(shí)引發(fā),通過該方法為HttpApplication事件調(diào)用相應(yīng)的事件委托;

  ·Dispose(),當(dāng)相應(yīng)請(qǐng)求處理結(jié)束并發(fā)送回IIS調(diào)用此方法,通過此方法執(zhí)行最終所有的清理和回收程序。

  為了更加方便地為網(wǎng)址重寫創(chuàng)建HttpModule,從一開始我就創(chuàng)建一個(gè)抽象的基類(BaseModuleRewriter),該類實(shí)現(xiàn)了IHttpModule接口。在Init(HttpApplication)事件中,它通過BaseModuleRewriter_AuthorizeRequest方法引發(fā)了HttpApplicationAuthorizeRequest事件,該BaseModuleRewriter_AuthorizeRequest方法通過該類的Rewrite()方法重寫傳入?yún)?shù)HttpApplication對(duì)象的內(nèi)部請(qǐng)求虛擬路徑(Path)。在BaseModuleRewriter對(duì)象中,該Rewrite()方法是抽象的,并且沒有實(shí)際內(nèi)容,但在繼承自該類的對(duì)象中必須重載Rewrite()方法并為該方法提供實(shí)際內(nèi)容。

  通過對(duì)該基類的繼承,所有需要做的工作就是創(chuàng)建一個(gè)繼承自BaseModuleRewriter的類,重載Rewrite()方法并在該方法中添加網(wǎng)址重寫邏輯代碼。下文列出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類將網(wǎng)址重寫放在AuthorizeRequest事件中調(diào)用,如果要使用Windows驗(yàn)證并使用文件驗(yàn)證模式時(shí)請(qǐng)修改代碼將網(wǎng)址授權(quán)放在BeginRequest或者AuthenticateRequest事件中。

  ModuleRewriter繼承自BaseModuleRewriter,并真正意義地實(shí)現(xiàn)了網(wǎng)址重寫的操作,該類僅包含一個(gè)重載了的方法Rewrite(),其內(nèi)容如下文所示:

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文件中的網(wǎng)址重寫規(guī)則的設(shè)置為起始,它通過循環(huán)訪問各條網(wǎng)址重寫規(guī)則,每次均獲取當(dāng)前規(guī)則中的LookFor屬性,用正則表達(dá)式驗(yàn)證并判斷是否查找是否對(duì)當(dāng)前請(qǐng)求的網(wǎng)址是否有匹配。

  如果發(fā)現(xiàn)一條匹配,將用當(dāng)前規(guī)則的SendTo值對(duì)請(qǐng)求的路徑執(zhí)行一個(gè)正則表達(dá)式替換,替換后的地址通過參數(shù)的形式傳給RewriterUtils.RewriteUrl()方法,RewriterUtils是一個(gè)幫助類,它提供一對(duì)HttpModuleHttpHandler都可以使用的靜態(tài)方法,RewriterUrl()方法只是簡單地調(diào)用了HttpContext對(duì)象的RewritePath()方法。

  注意:你已經(jīng)注意到了當(dāng)執(zhí)行正則表達(dá)式匹配和替換的時(shí)候調(diào)用了一個(gè)RewriterUtils.ResolveUrl()方法。該幫助方法簡單地替換了應(yīng)用程序路徑中“~”的所有實(shí)例。

  我們已經(jīng)探討了主要的部分,但是還有其它一些組件諸如將web.config文件中XML格式化了的網(wǎng)址重寫規(guī)則反序列化至一個(gè)對(duì)象的類定義、通過HttpHandlerFactory實(shí)現(xiàn)網(wǎng)址重寫的類定義等。本文最后三節(jié)將通過一些真實(shí)案例來探討網(wǎng)址重寫的技術(shù)。

  3. 用網(wǎng)址重寫引擎實(shí)現(xiàn)簡單的網(wǎng)址重寫

  為了更好地示范網(wǎng)址重寫引擎的運(yùn)行,我們來建立一個(gè)ASP.NET Web應(yīng)用程序來實(shí)現(xiàn)簡單的網(wǎng)址重寫引擎。假定我們?yōu)橐患以诰€銷售各類商品的公司服務(wù),這些產(chǎn)品劃分為以下類別:

  分類編號(hào)(CategoryID) 分類名稱(CategoryName)
   1          飲料(Beverages)
   2          調(diào)味品(Condiments)
   3          工藝品(Confections)
   4          日記本(Diary Products)
   ... ...
  
   假定已經(jīng)建立好一個(gè)名為ListProductsByCategoryID.aspx的ASP.NET頁面文件,它通過查詢參數(shù)獲取一個(gè)分類編號(hào),并根 據(jù)此編號(hào)獲取所有該分類下的所有商品。如果用戶想瀏覽所銷售的飲料類商品可以通過 ListProductsByCategoryID.aspx?CategoryID=1來訪問,如果用戶想瀏覽所銷售的日記本類商品可以通過 ListProductsByCategoryID.aspx?CategoryID=4來訪問。假定還有一個(gè)頁面 ListCategories.aspx,它列出所有代售商品的分類編號(hào)。

  顯然這里發(fā)現(xiàn)了一個(gè)網(wǎng)址重寫的案例。對(duì)于用戶來說他們所輸 入的地址不具有任何實(shí)際意義并且不具備任何“隱蔽性”,倒不如使用網(wǎng)址重寫引擎讓用戶去訪問/Products/Baverage.aspx地址,系統(tǒng)將 該地址重寫到ListProductsByCategoryID.aspx?CategoryID=1。我們可以在web.config文件中來完成網(wǎng)址 重寫任務(wù):

<RewriterConfig>
<Rules>
<! —- Rules for products lister -->
<RewriterRule>
<LookFor>~/Products/Baverage.aspx</LookFor>
<SendTo>~/ListProductsByCategoryID.aspx?CategoryID=1</SendTo>
</RewriterRule>
</Rules>
</RewriterConfig>

  很明顯地看到,搜索用戶訪問的路徑是否匹配/Products/Baverage.aspx,如果匹配的話,則將網(wǎng)址重寫到/ListProductsByCategoryID.aspx?CategoryID=1。

   注意:你會(huì)發(fā)現(xiàn)<LookFor>節(jié)點(diǎn)中避免直接在“Baverage.aspx”中使用句點(diǎn)“.”是因?yàn)?#xff1c;LookFor>節(jié)點(diǎn)的值是正則表達(dá)式的匹 配模式,在正則表達(dá)式中句點(diǎn)符號(hào)是一個(gè)特殊字符,它表示匹配任何一個(gè)字符,也就是說如果訪問BaverageQaspx時(shí)也會(huì)發(fā)生匹配,為了避免發(fā)生這個(gè) 句點(diǎn)引起的匹配我們得在該句點(diǎn)符號(hào)前面加上一個(gè)“\”,表示引用句點(diǎn)符號(hào)。

  通過該規(guī)則定義,當(dāng)用戶訪問/Products /Baverage.aspx文件的時(shí)候,他們將看到代售的飲料類商品列表信息。圖3為訪問/Products/Baverage.aspx地址時(shí)的瀏覽 器截圖,注意在瀏覽器中地址欄上顯示的是用戶輸入的/Products/Baverage.aspx地址,但是實(shí)際訪問的地址卻是網(wǎng)址重寫后的 /ListProductsByCategoryID.aspx?CategoryID=1。(事實(shí)上,在服務(wù)器上根本就不存在/Products /Baverage.aspx文件!)


圖三.網(wǎng)址重寫后的對(duì)商品分類的請(qǐng)求

   和/Products/Baverage.aspx類似,下一步我們添加其它分類的重寫規(guī)則,只需簡單地在web.config文件中<Rules>中 在添加其他<RewriteRule>節(jié)即可。該演示完整的重寫規(guī)則集合請(qǐng)參考下載文檔的web.config文件中的定義。

  為了讓 該網(wǎng)址更具有“隱蔽性”,如果讓用戶把/Products/Baverage.aspx后面Baverage.aspx一段截去,在瀏覽器中輸入 /Products/來瀏覽產(chǎn)品分類列表會(huì)更好一些。乍一看,這項(xiàng)任務(wù)微不足道,只需添加一條網(wǎng)址重寫規(guī)則將/Products/映射到 /ListCategories.aspx即可。然而這里有一個(gè)微妙之處,你必須先創(chuàng)建一個(gè)/Products/目錄,并在里面放一個(gè)空文件 Default.aspx。

  要認(rèn)識(shí)為什么這些額外的步驟是必須的,先回顧一下前文。網(wǎng)址重寫引擎是處于ASP.NET一級(jí)的,也就是 說,如果ASP.NET沒有獲得處理請(qǐng)求的機(jī)會(huì)的話,網(wǎng)址重寫引擎就不能對(duì)輸入的網(wǎng)址請(qǐng)求作出判斷。此外,IIS僅在請(qǐng)求文件包含相應(yīng)擴(kuò)展名時(shí)才將請(qǐng)求轉(zhuǎn) 交給ASP.NET引擎。如果用戶訪問/Products/,IIS并不知道其擴(kuò)展名是什么,于是它檢查該目錄下的文件看是否包含有默認(rèn)首頁文件名 (Default.aspx,Default.htm,Default.asp,等等,這些文件名在IIS管理工具對(duì)話框中Web服務(wù)器屬性對(duì)話框中的文 檔標(biāo)簽中定義。)當(dāng)然,如果/Products/目錄不存在的話,IIS將返回一個(gè)HTTP 404錯(cuò)誤。

  所以我們需要?jiǎng)?chuàng)建一個(gè)/Products/目錄并在該目錄下額外創(chuàng)建一個(gè)空文件Default.aspx,IIS會(huì)檢查該目錄下的文件,發(fā)現(xiàn)有一個(gè)默認(rèn)文件名Default.aspx,于是將請(qǐng)求轉(zhuǎn)交給ASP.NET,這樣,網(wǎng)址重寫引擎才能生效。

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

  通過該規(guī)則,用戶訪問/Products/Default.aspx或者訪問/Products/都可以看到如圖四所示的產(chǎn)品分類列表。


圖四.在網(wǎng)址上添加“隱蔽性”4.處理回送數(shù)據(jù)

  如果要重寫的網(wǎng)址上包含有服務(wù)器端Web Form并執(zhí)行數(shù)據(jù)回送,當(dāng)該Web Form回送數(shù)據(jù)時(shí)會(huì)暴露出真實(shí)的網(wǎng)址,也就是說,當(dāng)用戶訪問/Products/Baverage.aspx時(shí),瀏覽器上地址欄顯示的也是 /Products/Baverage.aspx,但是實(shí)際上是訪問 /ListProdutsByCategoryID.aspx?CategoryID=1的內(nèi)容,如果 ListProductsByCategoryID.aspx頁面執(zhí)行了數(shù)據(jù)回送的話,用戶被數(shù)據(jù)回送定向給原始的 /ListProductByCategoryID.aspx?CategoryID=1頁面上,而不是/Products/Baverage.aspx 頁面。這雖然不是什么大問題,但是用戶會(huì)覺察到點(diǎn)擊一個(gè)按鈕時(shí)網(wǎng)址發(fā)生了的變化,這也許會(huì)令人不安,因?yàn)槿绻鲇诰W(wǎng)址安全的角度來說,直接把真實(shí)的網(wǎng)址暴 露出來了。
之所以發(fā)生這種現(xiàn)象的原因是當(dāng)Web Form在呈現(xiàn)之時(shí)就明確地設(shè)置其action屬性為當(dāng)前Request對(duì)象中文件路徑的值。當(dāng)然,在Web Form呈現(xiàn)之時(shí),從/Produts/Baverage.aspx到/ListProductsByCategoryID.aspx?CategoryID=1的網(wǎng)址重寫就已經(jīng)執(zhí)行完畢了,這意味著Request對(duì) 象所匯報(bào)的是當(dāng)前用戶所訪問的地址是/ListProductsByCategoryID.aspx?CategoryID=1。這么看來,只需讓該服務(wù) 器端表單在呈現(xiàn)之時(shí)不呈現(xiàn)action屬性即可解決問題了。(對(duì)瀏覽器來說,如果不設(shè)置action屬性的話,那么在提交的時(shí)候?qū)⑹褂闷淠J(rèn)值。)

  然而不幸的是該Web Form不會(huì)允許你指定action屬性,也不會(huì)允許你通過設(shè)置一些屬性來達(dá)到禁用呈現(xiàn)action屬性的目的。得自行繼承System.Web.HtmlControls.HtmlForm這個(gè)類,并重載該類的RenderAttribute()方法,明確指出該類不呈現(xiàn)acton屬性。

  感謝繼承這個(gè)強(qiáng)大的功能,使得我們很簡單就獲取了HtmlForm這個(gè)類下所有的功能定義,只需少量幾行代碼就達(dá)到所需目的,完整代碼如下所示:

namespace ActionlessForm
{
public class Form:System.Web.UI.HtmlControls.HtmlForm
{
protected override void RenderAttributes(System.Web.UI.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",this.ClientID);
}
}
}
}

  對(duì)RenderAttributes()方法重載的代碼包含了原類HtmlFormRenderAttributes()方法全部的代碼內(nèi)容,只是簡單地去掉了設(shè)置action屬性這一節(jié)。

  當(dāng)創(chuàng)建并編譯了這個(gè)類后,將其添加到引用目錄即可在該ASP.NET Web應(yīng)用程序中使用。為了將原有HtmlForm類替換,只需簡單地在頁面頂部添加下列代碼:

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

  然后將<Form runat=”server”>標(biāo)簽替換為

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

  并將結(jié)束標(biāo)記</Form>替換為

<skm:Form>

  你可以查看該文檔相關(guān)下載中的ListProductsByCategoryID.aspx文件中的自定義Web Form,該下載已經(jīng)提供了完整的Visual Studio.NET項(xiàng)目文件包。

  注意:如果你打算進(jìn)行網(wǎng)址重寫的地址不執(zhí)行數(shù)據(jù)回送,則沒有必要使用該自定義Web Form的類。

  創(chuàng)建真正“隱蔽”的網(wǎng)址

  上一節(jié)簡單網(wǎng)址重寫的示例展示了如何通過新的網(wǎng)址重寫規(guī)則來輕松地配置網(wǎng)址重寫引擎,本節(jié)將通過出眾的正則表達(dá)式來展示網(wǎng)址重寫的強(qiáng)大威力。

   時(shí)下正在流行Blog,很多人都擁有一個(gè)自己的Blog。不論你是否對(duì)Blog感到陌生,他們正在不斷地更新自己的Blog頁面,這些頁面就像一個(gè)個(gè)人 日記本一樣。大多數(shù)Bloger只是簡單地記錄每天發(fā)生的事情,也有一些聚焦于某一主題,比如影評(píng)、球迷組織、電腦技術(shù)等。

  Blog 可以在任何地點(diǎn)由作者進(jìn)行更新,更新次數(shù)可以是一天多次,也可以是一周一兩次。在Blog頁面上只顯示最近10條更新,但事實(shí)上所有的Blog軟件都提供 了存檔記錄,訪客可以閱讀其歷史記錄。有了“隱蔽”的網(wǎng)址,Blog應(yīng)用程序?qū)⒆兊酶訌?qiáng)大。假定你通過/2004/02/14.aspx來查詢自己的 Blog上的文章,你會(huì)為閱讀到2004年2月14日的Blog感到驚訝嗎?此外你可能為了訪問2004年2月所有的Blog而將該地址裁減為/2004 /02/,要訪問2004年所有的Blog,你可能會(huì)試著去訪問/2004/。

  在維護(hù)一個(gè)Blog的時(shí)候,如果將這種具有“隱蔽性”的網(wǎng)址提供給用戶將會(huì)更好。實(shí)際上很多Blog引擎都提供了這種網(wǎng)址重寫的功能,現(xiàn)在來看看這些是如何通過網(wǎng)址重寫實(shí)現(xiàn)的。

   首先,我們需要一個(gè)頁面能夠分別按照年、月、日分別顯示Blog的內(nèi)容。假定現(xiàn)在已經(jīng)做好了一個(gè)頁面文件ShowBlogContent.aspx,它 能分別獲取年、月、日的查詢參數(shù),要查看2004年2月14日所發(fā)的帖子,我們可以訪問 /ShowBlogContent.aspx?year=2004&month=2&day=14,要瀏覽2004年2月的數(shù)據(jù)可以訪問 /ShowBlogContent.aspx?year=2004&month=2,要查詢2004年所有數(shù)據(jù)可以訪問 /ShowBlogContent.aspx?year=2004。(在下載文件中提供ShowBlogContent.aspx源代碼。)

   然后,當(dāng)用戶訪問/2004/02/14.aspx時(shí),我們需要將他訪問的網(wǎng)址重寫到 /ShowBlogContent.aspx?year=2004&month=2&day=14上。這里需要制定三條網(wǎng)址重寫規(guī)則:當(dāng) 指定訪問年月日時(shí)、當(dāng)指定訪問年月時(shí)和當(dāng)指定訪問年時(shí)。

<RewriterConfig>
<Rules>
<!-- Rules for Blog Content Displayer -->
<RewriterRule>
<LookFor>~/(d{4})/(d{2})/(d{2}).aspx</LookFor>
<SendTo>~/ShowBlogContent.aspx?year=$1&month=$2&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>

   這些網(wǎng)址重寫規(guī)則展示了正則表達(dá)式的強(qiáng)大威力。第一條規(guī)則按照(\d{4})/(\d{2})/(\d{2})\.aspx模式進(jìn)行查找,通俗的說,它 查找是否包含匹配xxxx/xx/xx.aspx格式的字符串,其中x表示數(shù)字,每一組數(shù)字必須用圓括號(hào)括起來,這樣可以在相應(yīng)<SendTo>節(jié)內(nèi)引用 圓括號(hào)內(nèi)的匹配字符串。我們可以使用$1、$2、$3來分別引用前面匹配的圓括號(hào)組,其中$1,$2,$3分別表示所匹配的第一、第二、第三個(gè)圓括號(hào)組。

   注意:由于web.config是XML格式的文檔,所以在文本域內(nèi)必須回避直接使用一些特殊字符,如:&,<和>符號(hào)等。在第一條網(wǎng)址重寫規(guī) 則的<SendTo>節(jié)中用&amp來表示引用&符號(hào),在第二條網(wǎng)址重寫規(guī)則的<SendTo>節(jié)中用<![CDATA[...]]>元 素來表示其中所有的內(nèi)容都是文本域,不再需要用轉(zhuǎn)義字符來表示引用。這兩種方法都可以實(shí)現(xiàn)同樣的目的。

  下面圖五、圖六、圖七都顯示出網(wǎng)址重寫的運(yùn)行狀況。這些數(shù)據(jù)都真實(shí)地摘自作者的Blog,圖五顯示2003年11月7日的帖子,圖六顯示所有2003年11月的帖子,圖七顯示2003年所有帖子。


圖五.顯示2003年11月7日的帖子


圖六. 顯示2003年11月所有的帖子


圖七. 顯示2003年所有的帖子

  注意:要使用網(wǎng)址重寫引擎,強(qiáng)烈推薦在<LookFor>節(jié)中使用正則表達(dá)式。

創(chuàng)建必須的目錄結(jié)構(gòu)

  當(dāng)IIS接收到對(duì)/2004/03/19.aspx的請(qǐng)求時(shí),他發(fā)現(xiàn)文件擴(kuò)展名.aspx,便將該 請(qǐng)求轉(zhuǎn)交給ASP.NET引擎處理,在ASP.NET 引擎中傳遞時(shí),該地址被重寫到/ShowBlogContent.aspx?year=2004&month=3&day=19,最后用 戶將看到該Blog上2004年3月19日所有的帖子,但是在用戶訪問/2004/03/時(shí)會(huì)發(fā)生什么呢?除非已經(jīng)存在一個(gè)/2004/01/的目錄,否 則IIS將返回一個(gè)404錯(cuò)誤,而且該目錄下還必須要有一個(gè)默認(rèn)頁面Default.aspx,IIS才能將請(qǐng)求轉(zhuǎn)交給ASP.NET引擎處理。

   通過這種方法你得手動(dòng)為每一年的Blog創(chuàng)建一個(gè)年份的目錄并在該年份下放置一個(gè)默認(rèn)文件Default.aspx,而且還得在該年份目錄下創(chuàng)建每一月 的目錄,從01、02、...、12,每一個(gè)目錄下也要防止一個(gè)默認(rèn)文件Default.aspx。(回想前面的例子,為了將/Products/重寫到 /ListCategories.aspx也是要建立一個(gè)/Products/目錄并放置一個(gè)默認(rèn)Default.aspx文件。

  很 明顯,這樣創(chuàng)建目錄結(jié)構(gòu)的過程是很痛苦的。解決這種問題的一個(gè)辦法就是設(shè)置IIS將所有接收的請(qǐng)求都轉(zhuǎn)交給ASP.NET引擎來處理,這種方法,甚至連訪 問這種地址/2004/04/,IIS都如實(shí)地將其轉(zhuǎn)交給ASP.NET引擎處理,這種方法造成ASP.NET引擎得處理所有傳入的請(qǐng)求,包括css文 件,圖片文件、Javascript文件以及Flash文件等等。

  關(guān)于對(duì)所有類型文件的處理的詳細(xì)討論已經(jīng)超出了本書范圍。有關(guān)在ASP.NET Web應(yīng)用程序中使用這些技術(shù)的例子請(qǐng)?jiān)L問.Text 這個(gè)開源的Blog。.Text 可以通過配置將所有請(qǐng)求都轉(zhuǎn)交給ASP.NET處理。它使用了一個(gè)自定義的HttpHandler來處理所有類型的文件類型,這個(gè)自定義的HttpHandler可以識(shí)別并判斷如何處理所有的文件類型。(圖像文件、CSS文件等等。)

  結(jié)束語

  本文探討了通過類HttpContext類的RewriteUrl()方法來實(shí)現(xiàn)ASP.NET一級(jí)的網(wǎng)址重寫,正如我們所看到那樣,RewriteUrl()方法在修改這個(gè)特有的HttpContextRequest的屬性時(shí)也修改了所請(qǐng)求的文件和路徑。實(shí)際得到的效果就是在用戶訪問其特有的網(wǎng)址的時(shí)候,他實(shí)際卻是在服務(wù)器端請(qǐng)求另一個(gè)與此不同的網(wǎng)址。

  網(wǎng)址重寫不但可以在HttpModule中執(zhí)行,也可以在HttpHandler中運(yùn)行。本文我們探討了在一個(gè)HttpModule中執(zhí)行網(wǎng)址重寫,也研究了一下網(wǎng)址重寫在ASP.NET中的各個(gè)不同場(chǎng)所的情況。

   當(dāng)然,在ASP.NET一級(jí)的網(wǎng)址重寫中,只有在IIS成功地將請(qǐng)求轉(zhuǎn)交給ASP.NET引擎后才能成功地執(zhí)行,當(dāng)用戶請(qǐng)求一個(gè)擴(kuò)展名為.aspx的文 件時(shí)這很自然地發(fā)生。然而,如果要讓用戶輸入一個(gè)實(shí)際并不存在的網(wǎng)址,通過網(wǎng)址重寫到另一個(gè)存在的aspx頁面,你必須為該請(qǐng)求創(chuàng)建相應(yīng)的目錄和默認(rèn)的 Default.aspx頁面,除非配置IIS讓它把所有的請(qǐng)求都轉(zhuǎn)交給IIS處理,但是這種方式盲目地將所有請(qǐng)求都轉(zhuǎn)交給了ASP.NET引擎。 ?

轉(zhuǎn)載于:https://www.cnblogs.com/chenying99/archive/2011/03/13/1982544.html

總結(jié)

以上是生活随笔為你收集整理的在ASP.NET中实现Url Rewriting的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。