這篇文章描述了一個完整的 ASP.NET 2.0 URL 重寫方案。這個方案使用正則表達(dá)式來定義重寫規(guī)則并解決通過虛擬 URLs 訪問頁面產(chǎn)生回發(fā)事件的一些可能的困難。
為什么要重寫 URL ?將 URL 重寫方法應(yīng)用到你的 ASP.Net 應(yīng)用程序的兩個主要原因是:可用性和可維護(hù)性。
可用性誰都知道,相對于難于辨認(rèn)的帶參數(shù)的長的查詢路徑,用戶更喜歡一些短的、簡潔的 URL。任何時候,一個容易記住和敲入的路徑比添加到收藏夾更有用。其次,當(dāng)一個瀏覽器的收藏夾不可用時,記住的地址總比在搜索引擎中輸入關(guān)鍵字進(jìn)行搜索,然后再查找要強(qiáng)的多。比較下面的兩個地址:
(1)
?Year=2006&Month=12&Day=10
(2)
somebloghost.com/Blogs/2006/12/10/
第一個 URL 包含了查詢字符串;第二個URL包含的信息可以讓用戶清楚的看到他看的東西,它還可以使用戶更容易的修改地址欄的內(nèi)容,如:
可維護(hù)性在很多WEB應(yīng)用程序中,開發(fā)人員經(jīng)常會將頁面從一個目錄移到另一個目錄,讓我們假設(shè)一開始有兩個可用頁面: 和 ,但是后來開發(fā)者將 Copyright.aspx 和 Contacts.aspx 移到了 Help 目錄,用戶收藏起來地址就需要重新定位。這個問題雖然可以簡單的用 Response.Redirect(new location) 來解決,但是如果有成百上千的頁面呢?應(yīng)用程序中就會包含大量的無效鏈接。
使用 URL 重寫,允許用戶只需修改配置文件,這種方法可以讓開發(fā)者將web應(yīng)用程序邏輯結(jié)構(gòu)與物理結(jié)構(gòu)獨立開來。
ASP.NET 2.0 中的原有的URL 映射ASP.NET 2.0 為 web 應(yīng)用程序提供了一個開箱即用的映射靜態(tài) URL 的解決方案。這個方案不用編寫代碼就可以在 web.config 中將舊的 URLs 映射到新的地址。 要使用 URL 映射,只需在 web.config 文件的 system.web 節(jié)中創(chuàng)建一個新的 urlMappings 節(jié) ,并添加要映射的地址 (“ ~/ ”指向應(yīng)用程序的根目錄):
<urlMappings enabled="true">
<add url="~/Info/Copyright.aspx" mappedUrl="~/Help/Copyright.aspx" />
<add url="~/Support/Contacts.aspx" mappedUrl="~/Help/Contacts.aspx" />
</urlMappings>
這樣,如果用戶輸入 , 它將看到 , 而他并不知道那個頁已經(jīng)移除。
這個方案對于只有兩個頁面被移到其它位置的情況是足夠的。但它對有一打的需要重定位的頁或者需要創(chuàng)建一個整潔的URL來說,它是不合適的。另一個使用Asp.Net 的原有的URL映射技術(shù)的不太好的地方是:如果 Contacts.aspx 頁包含的元素在回發(fā)到服務(wù)器時(這是非??赡艿?, 用戶將會驚奇的發(fā)現(xiàn)地址 卻變成了
。 這是因為ASP.NET 引擎用頁面的實際地址修改了表單form 的 action 屬性 ,所以表單就變成了下面的樣子:
<form method="post"
action="http://www.simple-talk.com/Help/Contacts.aspx">
</form>
這樣看來,URL 映射在ASP.NET 2.0 中幾乎是無用的。我們應(yīng)當(dāng)能夠使用一個映射規(guī)則來指定一系列相似的 URL。最好的解決方案就是使用正則表達(dá)式 ( Wikipedia 上可以查看概覽,and 在 .NET 下的實現(xiàn)可以查看 MSDN), 但由于 ASP.NET 2.0 映射不支持正則表達(dá)式,所以我們需要開發(fā)一個內(nèi)建到 URL 映射的不同的方案- URL 重寫模塊。 最好的方法就是創(chuàng)建一個可重用的、簡單的配置模塊來實現(xiàn),顯然我們應(yīng)創(chuàng)建一個 HTTP 模塊 (關(guān)于 HTTP 模塊的詳細(xì)信息請查看 MSDN 雜志) 并在獨立的程序集中實現(xiàn)。要使這個程序集簡單易用,我們應(yīng)實現(xiàn)這個重寫引擎的可配置性,即能夠在 web.config 中指定規(guī)則。
在開發(fā)過程中,我們應(yīng)能使這個重寫模塊打開或關(guān)閉 (比如你有一個較難捕獲的bug,而它可能是由不正確的重寫模塊引起的)這樣在 web.config 中對重寫模塊的配置節(jié)進(jìn)行打開或關(guān)閉就成為一個選擇。這樣,在 web.config 中,一個配置節(jié)的示例如下:
<rewriteModule>
<rewriteOn>true</rewriteOn>
<rewriteRules>
<rule source="(/d+)/(/d+)/(/d+)/"
destination="Posts.aspx?Year=$1&Month=$2&Day=$3"/>
<rule source="(.*)/Default.aspx"
destination="Default.aspx?Folder=$1"/>
</rewriteRules>
</rewriteModule>
這樣,所有像: 這樣的請示,將會在內(nèi)部將會用帶參數(shù)的請求重定向到 Posts.aspx 。
請注意: web.config 是一個結(jié)構(gòu)良好的 XML 文件, 它禁止在屬性值中使用 & 符號,所以在例子中,應(yīng)當(dāng)使用 & 代替。
要在配置文件中使用這個重寫模塊,還需要注冊節(jié)和指定處理模塊,像下面這樣增加一個configSections配置節(jié):
<configSections><sectionGroup>
<section type="RewriteModule.
RewriteModuleSectionHandler, RewriteModule"/>
</sectionGroup>
</configSections>
這樣你就可以在 configSections 節(jié)的后面這樣使用了:
<modulesSection>
<rewriteModule>
<rewriteOn>true</rewriteOn>
<rewriteRules>
<rule source="(/d+)/(/d+)/(/d+)/" destination="Post.aspx?Year=$1&Month=$2&Day=$3"/>
<rule source="(.*)/Default.aspx" destination="Default.aspx?Folder=$1"/>
</rewriteRules>
</rewriteModule>
</modulesSection>
另一個我們在開發(fā)重寫模塊過程中要做的就是還需要允許在虛擬路徑中傳遞參數(shù),象這樣: ?Sort=Desc&SortBy=Date 。所以我們還需要有一個檢測通過虛擬 URL 傳遞參數(shù)的解決方案。
接下來讓我們來創(chuàng)建類庫。首先,我們要引用 System.Web 程序集,這樣我們可以實現(xiàn)一些基于 web 特殊功能。如果要使我們的模塊能夠訪問 web.config,還需要引用 System.Configuration 程序集。
處理配置節(jié)
要能處理 web.config 中的配置,我們必需創(chuàng)建一個實現(xiàn)了 IConfigurationSectionHandler 接口的類 (詳情查看 MSDN )。如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Web;
using System.Xml;
namespace RewriteModule
{
public class RewriteModuleSectionHandler : IConfigurationSectionHandler
{
private XmlNode _XmlSection;
private string _RewriteBase;
private bool _RewriteOn;
public XmlNode XmlSection
{
get { return _XmlSection; }
}
public string RewriteBase
{
get { return _RewriteBase; }
}
public bool RewriteOn
{
get { return _RewriteOn; }
}
public object Create(object parent,
object configContext,
System.Xml.XmlNode section)
{
// set base path for rewriting module to
// application root
_RewriteBase = HttpContext.Current.Request.ApplicationPath + "http://m.survivalescaperooms.com/";
// process configuration section
// from web.config
try
{
_XmlSection = section;
_RewriteOn = Convert.ToBoolean(
section.SelectSingleNode("rewriteOn").InnerText);
}
catch (Exception ex)
{
throw (new Exception("Error while processing RewriteModule
configuration section.", ex));
}
return this;
}
}
}
RewriteModuleSectionHandler 類將在 web.config 中的 XmlNode 通過調(diào)用 Create 方法初始化。XmlNode 類的 SelectSingleNode 方法被用來返回模塊的配置值。
使用重寫的 URL 的參數(shù)在處理象 http://www. somebloghost.com/Blogs/gaidar/?Sort=Asc (這是一個帶參數(shù)的虛擬 URL ) 虛擬的 URLS 時,能夠清楚的辨別通過虛擬路徑傳遞的參數(shù)是非常重要的,如下:
<rule source="(.*)/Default.aspx" destination="Default.aspx?Folder=$1"/>,
你可能使用這樣的 URL:
somebloghost.com/gaidar/?Folder=Blogs
它的效果和下面的相似:
somebloghost.com/Blogs/gaidar/
要處理這個問題,我們需要對'虛擬路徑參數(shù)' 進(jìn)行包裝。這可以是通過一個靜態(tài)的方法去訪問當(dāng)前的參數(shù)集:
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using System.Web;
namespace RewriteModule
{
public class RewriteContext
{
// returns actual RewriteContext instance for
// current request
public static RewriteContext Current
{
get
{
// Look for RewriteContext instance in
// current HttpContext. If there is no RewriteContextInfo
// item then this means that rewrite module is turned off
if(HttpContext.Current.Items.Contains("RewriteContextInfo"))
return (RewriteContext)
HttpContext.Current.Items["RewriteContextInfo"];
else
return new RewriteContext();
}
}
public RewriteContext()
{
_Params = new NameValueCollection();
_InitialUrl = String.Empty;
}
public RewriteContext(NameValueCollection param, string url)
{
_InitialUrl = url;
_Params = new NameValueCollection(param);
}
private NameValueCollection _Params;
public NameValueCollection Params
{
get { return _Params; }
set { _Params = value; }
}
private string _InitialUrl;
public string InitialUrl
{
get { return _InitialUrl; }
set { _InitialUrl = value; }
}
}
}
可以看到,這樣就可以通過RewriteContext.Current 集合來訪問 “虛擬路徑參數(shù)”了,所有的參數(shù)都被指定成了虛擬路徑或頁面,而不是像查詢字符串那樣了。
重寫 URL接下來,讓我們嘗試重寫。首先,我們要讀取配置文件中的重寫規(guī)則。其次,我們要檢查那些在 URL 中與規(guī)則不符的部分,如果有,進(jìn)行重寫并以適當(dāng)?shù)捻搱?zhí)行。
創(chuàng)建一個 HttpModule:
class RewriteModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{}
}
當(dāng)我們添加 RewriteModule_BeginRequest 方法以處理不符合規(guī)則的 URL時,我們要檢查給定的 URL 是否包含參數(shù),然后調(diào)用 HttpContext.Current.RewritePath 來進(jìn)行控制并給出合適的 ASP.NET 頁。
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Configuration;
using System.Xml;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.IO;
using System.Collections.Specialized;
namespace RewriteModule
{
class RewriteModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
// it is necessary to
context.BeginRequest += new EventHandler(
RewriteModule_BeginRequest);
}
void RewriteModule_BeginRequest(object sender, EventArgs e)
{
RewriteModuleSectionHandler cfg =
(RewriteModuleSectionHandler)
ConfigurationManager.GetSection
("modulesSection/rewriteModule");
// module is turned off in web.config
if (!cfg.RewriteOn) return;
string path = HttpContext.Current.Request.Path;
// there us nothing to process
if (path.Length == 0) return;
// load rewriting rules from web.config
// and loop through rules collection until first match
XmlNode rules = cfg.XmlSection.SelectSingleNode("rewriteRules");
foreach (XmlNode xml in rules.SelectNodes("rule"))
{
try
{
Regex re = new Regex(
cfg.RewriteBase + xml.Attributes["source"].InnerText,
RegexOptions.IgnoreCase);
Match match = re.Match(path);
if (match.Success)
{
path = re.Replace(
path,
xml.Attributes["destination"].InnerText);
if (path.Length != 0)
{
// check for QueryString parameters
if(HttpContext.Current.Request.QueryString.Count != 0)
{
// if there are Query String papameters
// then append them to current path
string sign = (path.IndexOf('?') == -1) ? "?" : "&";
path = path + sign +
HttpContext.Current.Request.QueryString.ToString();
}
// new path to rewrite to
string rew = cfg.RewriteBase + path;
// save original path to HttpContext for further use
HttpContext.Current.Items.Add(
"OriginalUrl",
HttpContext.Current.Request.RawUrl);
// rewrite
HttpContext.Current.RewritePath(rew);
}
return;
}
}
catch (Exception ex)
{
throw (new Exception("Incorrect rule.", ex));
}
}
return;
}
}
}
這個方法必須注冊:
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(RewriteModule_BeginRequest);
}
但這些僅僅完成了一半,因為重寫模塊還要處理表單的回發(fā)和虛擬路徑參數(shù)集合,而這段代碼中你會發(fā)現(xiàn)并沒處理這些。讓我們先把虛擬路徑參數(shù)放到一邊,先來正確地處理最主要的回發(fā)。
如果我們運行上面的代碼,并通過查看 ASP.NET 的 HTML 源代碼 的 action 會發(fā)現(xiàn),它竟然包含了一個 ASP.NET 的實際路徑頁。例如,我們使用頁 ~/Posts.aspx 來處理像 somebloghost.com/Blogs/2006/12/10/Default.aspx 的請求, 發(fā)現(xiàn) action="/Posts.aspx"。這意味著用戶并沒有使用虛擬路徑進(jìn)行回發(fā),而是使用了實際的 somebloghost.com/Blog.aspx. 這個并不是我們需要的。所以,需要加一段代碼來處理這些不希望的結(jié)果。
首先,我們要在 HttpModule 注冊和實現(xiàn)一個另外的方法:
public void Init(HttpApplication context)
{
// it is necessary to
context.BeginRequest += new EventHandler(
RewriteModule_BeginRequest);
context.PreRequestHandlerExecute += new EventHandler(
RewriteModule_PreRequestHandlerExecute);
}
void RewriteModule_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
if ((app.Context.CurrentHandler is Page) &&
app.Context.CurrentHandler != null)
{
Page pg = (Page)app.Context.CurrentHandler;
pg.PreInit += new EventHandler(Page_PreInit);
}
}
這個方法檢查用戶是否請求了一個正常的 ASP.NET 頁,然后為該頁的 PreInit 事件增加處理過程。這兒 RewriteContext 將處理實際參數(shù),然后二次重寫URL。二次重寫是必需的,以使 ASP.NET 能夠在它的表單的action屬性中使用一個虛擬路徑。
void Page_PreInit(object sender, EventArgs e)
{
// restore internal path to original
// this is required to handle postbacks
if (HttpContext.Current.Items.Contains("OriginalUrl"))
{
string path = (string)HttpContext.Current.Items["OriginalUrl"];
// save query string parameters to context
RewriteContext con = new RewriteContext(
HttpContext.Current.Request.QueryString, path);
HttpContext.Current.Items["RewriteContextInfo"] = con;
if (path.IndexOf("?") == -1)
path += "?";
HttpContext.Current.RewritePath(path);
}
}
最后,我們來看一下在我們的重寫模塊程序集中的三個類:
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/1401545P0_0.gif)
要使用重寫模塊,需要在配置文件中的 httpModules 節(jié)注冊重寫模塊,如下:
<httpModules>
<add type="RewriteModule.RewriteModule, RewriteModule"/>
</httpModules>
使用重寫模塊在使用重寫模塊時,需要注意:
要使用帶擴(kuò)展的重寫模塊代替 .aspx (如 .html or .xml), 必須配置 IIS ,以使這些擴(kuò)展映射到 ASP.NET 引擎 (ASP.NET ISAPI 擴(kuò)展)。要進(jìn)行這些設(shè)置,需要以管理員身份登錄。
打開 IIS 管理控制臺,并選擇你要配置的站點的虛擬路徑:
Windows XP (IIS 5)
Virtual Directory "RW"
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/1401523610_1.gif)
Windows 2003 Server (IIS 6)
Default Web Site
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/1401523610_0.gif)
然后在虛擬路徑標(biāo)簽上點擊 Configuration… 按鈕 (或如果要使用整個站點都做映射就選擇主目錄標(biāo)簽)。
Windows XP (IIS 5)
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/1401513432_1.gif)
Windows 2003 Server (IIS 6)
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/1401513432_0.gif)
接下來,點擊添加按鈕,并輸入一個擴(kuò)展,你還需要指定一個 ASP.NET ISAPI 擴(kuò)展,注意去掉選項的對勾以檢查文件是否存在。
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/14015023W_1.gif)
如果你要把所有的擴(kuò)展都映射到 ASP.NET,對Windows XP上的 IIS 5 來說只需要設(shè)置 .* 到 ASP.NET ISAPI ,但對 IIS 6 就不一樣了,點擊“添加”然后指定 ASP.NET ISAPI 擴(kuò)展。
![一個完整的ASP.NET 2.0 URL重寫方案[翻譯] 一個完整的ASP.NET 2.0 URL重寫方案[翻譯]](http://m.survivalescaperooms.com/uploads/allimg/131223/14015023W_0.gif)
現(xiàn)在,我們已經(jīng)創(chuàng)建了一個簡單的但非常強(qiáng)大的 ASP.NET 重寫模塊,它支持可基于正則表達(dá)式的 URLs 和頁面回發(fā),這個解決方案是容易實現(xiàn)的,并且提供給用戶的例子也是可用的,它可以用簡短的、整潔的URL來替代查詢字符串參數(shù)。 要使用這個模塊,只需簡單在你的應(yīng)用程序中對 RewriteModule 進(jìn)行引用,然后在 web.config 文件中添加幾行代碼以使你不想顯示的 URL 通過正則表達(dá)式代替。這個重寫模塊是很容易部署的,因為只需要在web.config中修改任何“虛擬”的URL即可,如果你需要進(jìn)行測試,還可以對重寫模塊進(jìn)行關(guān)閉。
要想對重寫模塊有一個深入的了解,你可以查看本文提供的原代碼。我相信你會發(fā)現(xiàn)這是一個比ASP.NET提供的原始映射更好的體驗。
|
新聞熱點
疑難解答
圖片精選