使用 apache 時可以方便的使用 mod_rewrite,來做重寫網址的功能,而到了 IIS 則可使用 IIS URL Rewrite Module 來達成,而 .NET 3.5 SP1 之後新增加了 System.Web.Routing 的組件,他可以做到和 IIS URL Rewrite Module 類似的事情,但是在背後運作的機制和使用上有不同之處,在本篇就不多做介紹。

本篇將說明如何使用 Routing 組件來達成 URL 重寫的功能,例如 Blog.aspx?User=emn178 的網址,以 emn178/Blog 的樣式呈現,此篇文章以實作 IRouteHandler 方式處理。

開發模式

要在開發時套用此功能,依照以下步驟設定

  1. 專案 [加入參考] => [.NET] => [System.Web.Routing] 組件
  2. VS2008 不會自動將模組加入 Web.config,所以需要額外加入以下內容,請找到相對應的位置加入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<configuration>
<system.web>
<compilation debug="true">
<assemblies>
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</assemblies>
</compilation>
<httpModules>
<add name="RoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</modules>
<handlers>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</handlers>
</system.webServer>
</configuration>
  1. 實作 Handler,例如建立 BlogRouteHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BlogRouteHandler : IRouteHandler
{
private string virtualPath;

public BlogRouteHandler(string virtualPath)
{
this.virtualPath = virtualPath;
}

public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string origQueryStr = HttpContext.Current.Request.QueryString.ToString();
string queryStr = "?User=" + requestContext.RouteData.Values["User"];

if (!string.IsNullOrEmpty(origQueryStr))
queryStr += "&" + origQueryStr;
HttpContext.Current.RewritePath(string.Concat(virtualPath, queryStr));

return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as Page;
}
}
  1. 如果沒有 Global.asax 則[新增項目] => [全域應用程式類別]
  2. Global.asax 中加入 Routing 註冊函式
1
2
3
4
5
6
7
8
9
10
void Application_Start(object sender, EventArgs e)
{
// 應用程式啟動時執行的程式碼
RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route("{User}/blog", new BlogRouteHandler("~/Blog.aspx")));
}

說明
由以上範例可發現,new Route("{User}/blog", ...) 表示你的規則,大括弧表示會變動的部分,變動的部分會傳入 GetHttpHandler 中的 requestContext.RouteData

自訂更多的規則

假設現在要新增 Blog.aspx?User=emn178&ArticleID=123 變成 emn178/Blog/123 的規則,而原來的規則也同時存在,將 RegisterRoutes 修改為

1
2
3
4
5
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route("{User}/blog", new BlogRouteHandler("~/Blog.aspx")));
routes.Add(new Route("{User}/blog/{ArticleID}", new BlogRouteHandler("~/Blog.aspx")));
}

BlogRouteHandler 修改為

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BlogRouteHandler : IRouteHandler
{
private string virtualPath;

public BlogRouteHandler(string virtualPath)
{
this.virtualPath = virtualPath;
}

public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string origQueryStr = HttpContext.Current.Request.QueryString.ToString();
string queryStr = "?User=" + requestContext.RouteData.Values["User"];
if (requestContext.RouteData.Values["ArticleID"] != null)
queryStr += "?ArticleID=" + requestContext.RouteData.Values["ArticleID"];

if (!string.IsNullOrEmpty(origQueryStr))
queryStr += "&" + origQueryStr;
HttpContext.Current.RewritePath(string.Concat(virtualPath, queryStr));

return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as Page;
}
}

套用到 IIS6

Routing 的規則在 IIS6 下會變成目錄不存在而無法顯示網頁,可以用以下的方式讓他交由 ASP.NET 去處理

  1. 進入 IIS 管理頁面,在你的網頁應用程式按右鍵 => [內容]
  2. 於目錄的頁籤下點擊[設定]
IIS 設定
  1. 接著點擊[新增]來新增對應
  2. 執行檔選擇 .NET Framework 的 aspnet_isapi.dll,例如:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll
  3. 副檔名輸入 .*
  4. 指令動詞選擇[限制於]並輸入 GET,HEAD,POST,DEBUG
  5. 不勾選[檢查檔案是否存在]
IIS 設定

套用到 IIS7

IIS7 則不需額外設定就可使用