ASP.NET MVC 開發心得分享 (21):Routing 觀念與技巧
ASP.NET MVC 預設在 Global.asax 所定義的 RegisterRoutes 方法中可以輕易的定義你希望擁有的網址格式,嚴格上來講這並非 ASP.NET MVC 的專利,而是從 ASP.NET 3.5 SP1 就加入的新特性,所以就算是傳統的 ASP.NET Web Form 一樣可以利用 Routing 所帶來的好處,今天我就來講一些 Routing 的觀念與技巧。
快速上手
我先解釋在 ASP.NET MVC 專案中 Global.asax 所定義的 Routing 程式碼,這也是初學者很容易看不懂的地方。

以下文字用來描述上圖標號的部分:
- ASP.NET 執行的起點就在於 HttpApplication 的 Application_Start() 方法,所有 Routing 都會定義在此,其中 RouteTable.Routes 是一個公開的靜態物件,用來儲存所有的 Routing 規則,其物件型別為 RouteCollection。
- 在預設 RegisterRoutes 方法中的 IgnoreRoute 用來定義 不要透過 Routing 處理的網址。
註: IgnoreRoute 擴充方法是 ASP.NET MVC (System.Web.Mvc) 的一部份。 - {resource} 代表一個 路由變數(RouteValue),其名稱為 resource,但在這裡其實取任何名字都可以,這裡只代表一個變數空間 (PlaceHolder) 罷了。總之就是代表一個「位置」,可以放入一個用不到的變數。
- {*pathInfo} 也是代表一個 RouteValue 名稱為 pathInfo,但名稱前面的星號 ( * ) 代表 CatchAll的意思,這個名為 pathInfo 的 RouteValue 會是完整的 PATH INFO 扣除 標號 3 比對到的網址。例如:若網址是 /TEST.axd/a/b/c/d 則 pathInfo 所得到的值為 a/b/c/d,如果沒加上星號 ( * ) 該 pathInfo 就會等於 a 而已。而在這裡其實取任何名字都可以,這裡也只是代表一個變數的位置。
- MapRoute 則是最常用來定義 Routing 規則的擴充方法。
註: MapRoute 擴充方法是 ASP.NET MVC (System.Web.Mvc) 的一部份。 - 定義 Route 名稱,這個名稱雖然少用,但是開發到較為進階的 Routing 技巧時會用到。
- 定義 網址格式 與每個 網址段落(Path Segment) 的 RouteValue 參數名稱。
注意: 該網址不能以斜線 ( / ) 開頭。 - 定義各 RouteValue 參數的預設值
基本觀念
- Routing (System.Web.Routing) 是 ASP.NET 3.5 SP1 的一部份,ASP.NET MVC 只是使用而已
- RouteTable.Routes 是一個公開的靜態物件,在整個 HttpApplication 生命週期裡都會固定存在!
- 由於 RouteTable.Routes 是一個公開的靜態物件,所以在不用重新編譯網站的情況下是有可能動態調整 RouteTable.Routes 內容的。詳見:Editable Routes 與 Editable Routes Using App_Code
- IgnoreRoute 與 MapRoute 這兩個擴充方法 (ExtensionMethod) 是 System.Web.Mvc 命名空間中所新增的 RouteCollectionExtensions 類別所提供的擴充方法,為了方便撰寫 Routing 規則之用。
- 在 Routing 規則集之中,第一個被比對成功的規則就會直接被採用,此原則跟 Firewall Rules 類似
路由開發技巧
技巧 1:替 Routing 網址設立條件限制
由於 ASP.NET MVC 有個很重要的特性:以習慣取代設定 (Convention over Configuration),所以當我們從網址路徑對應到 Controller 的過程中,網址列上的路由變數會自動對應到 Action 方法的參數中,例如我們在 Global.asax.cs 檔案中有以下路由定義:
routes.MapRoute(
"Product",
"Product/{productId}",
new {controller="Product", action="Details"}
);
而我們的 Action 方法的內容如下:
public ActionResult Details(int productId)
{
return View();
}
因為我們 Action 方法裡的參數是 int 型別,所以傳入的 productId 路由變數必須要是「整數」,如果不是整數的話,就會導致 The parameters dictionary contains a null entry for parameter 'productId' of non-nullable type 'System.Int32' for method 'System.Web.ActionResult Details(Int32)' in ... 的錯誤發生。
要避免這種無效的網址路由比對,我們可以在定義路由時多下一些限制條件,來進一步比對 網址段落(PathSegment) 是否符合 路由變數(RouteValue) 的格式要求,我們以上述例子來進一步修改路由定義,如下程式範例,我們多增加了一個參數傳入 MapRoute 參數,其中 \d+ 是 正規表示式(RegEx) 樣式:
routes.MapRoute(
"Product",
"Product/{productId}",
new {controller="Product", action="Details"},
new {productId = @"\d+" }
);
注意:這裡的 RegEx 樣式在比對時會自動幫你加上 ^ 開頭與 $ 結尾,也代表這個樣式一定會比對完整的 網址段落(Path Segment)。
如此一來,當網址輸入的格式是 /Product/CellPhone 的時候,就不會比對到 "Product" 這一條路由規則。
技巧 2:自訂 Routing 條件限制
一般情況下透過 RegEx 正規表示式來定義 RouteValue 條件限制已經很足夠,但若遇到需要較為複雜的判斷情況時,就有可能要自訂 Routing 條件限制,自訂的方法是透過實做 IRouteConstraint 介面的方式來完成,該介面只有一個 Match 方法需要實做,以下是該方法的定義:
bool Match(
HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection
)
有了這些傳入的參數,你將可以取得許多在網址變數比對時可能得到的各種資訊,再拿這些取得的資料進行判斷,就可以有成千上百種條件的限制組合,任你怎樣寫都可以。例如你想設定一組只有在 Localhost 本機執行網站時才能使用的路由限制(Route Constraint),我們可以透過 httpContext.Request.IsLocal 判斷來源電腦是否來自於本機,這樣我們可以寫出以下程式碼:
using System.Web;
using System.Web.Routing;
namespace MvcApplication1.Constraints {
public class LocalhostConstraint : IRouteConstraint {
public bool Match (HttpContextBase httpContext, Route route,
string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
return httpContext.Request.IsLocal;
}
}
}
那麼我們在 Global.asax.cs 裡的路由規則又應該如何定義呢?請看以下程式碼:
routes.MapRoute(
"Admin",
"Admin/{action}",
new { controller="Admin" },
new { isLocal=new LocalhostConstraint() }
);
眼尖的人可能會發現我們在限制條件的地方使用了一個 isLocal 變數,但是該變數根本沒有出現在網址格式 之中出現過,但為什麼能用呢?這是要限制誰呢?真的有一個 isLocal 這個 RouteValue 變數嗎?
我相信初學者看到這些程式碼很可能會有這些疑問,因為當時的我看到這段 Code 也質疑了一下,事實上自訂路由限制套用在路由定義時,其匿名變數的屬性名稱一點也不重要,所有的判斷都是寫在自訂路由限制的程式碼裡,所以上述的路由定義如果我修改成以下,還是可以正常運作的,你自己取名的時候只要自己覺得好記、好懂即可:
routes.MapRoute(
"Admin",
"Admin/{action}",
new { controller="Admin" },
new { OnlyLocalhostCanApply=new LocalhostConstraint() }
);
技巧 3:讓 ASP.NET MVC 與 ASP.NET Web Form 和平共處
基於 ASP.NET MVC 與 ASP.NET Web Form 的運作方式不同,所以通常這兩種架構下的程式在預設的情況下都能正常的運作,只有在某些特殊的設定下才會兩邊互相干擾,當你網站部署時遇到互相干擾的狀況時,就可以套用以下 Routing 技巧,設定特定一些路徑不要走 ASP.NET MVC 的執行管線。
以下是一些過濾掉路由規則定義的程式碼範例:
- 忽略所有 *.aspx 的網址路徑
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}"); - 忽略所有在 Page 目錄下的所有程式與檔案 ( 請注意,這是忽略 Routing 而已,改由 IIS 來判斷要用何種 Handler 來處理這次 HTTP 要求 )
routes.IgnoreRoute("Page/{*pathInfo}"); - 忽略所有在 Page 目錄下的所有檔案 ( 另一種寫法 )
routes.Add(new Route("Page/{*pathInfo}", new StopRoutingHandler()));
另外還有一點也值得注意,也就是 Routing 的動作其實是在執行 HttpHandler 執行之前,他是一個專門用來分配 Request 的模組,所以可以透過 Routing 的定義與設定來決定到底後續要執行 ASP.NET MVC 還是 ASP.NET Web Form,在 IIS7 的 HTTP Request 的執行順序中,UrlRoutingModule (Routing module) 的執行順位如下圖示,執行事件名稱為 Resolve Cache 這一步之後(PostResolveRequestCache):

以下一些參考連結可以幫助你學習如何讓 ASP.NET Web Form 的網頁也套用 Routing 規則:
- Using Routing With WebForms
- URL Routing with ASP.NET 4 Web Forms (VS 2010 and .NET 4.0 Series)
- Extreme ASP.NET - Routing with ASP.NET Web Forms
技巧 4:讓 Routing 也對已存在的檔案做處理 (將所有網址都透過 Routing 進行比對)
當 HTTP 要求進來後,在 ASP.NET Routing 在預設的情況下,只要當輸入的網址路徑可以比對出在實體的檔案系統中的檔案,就會直接跳過 Routing 機制,這也是為什麼 ASP.NET MVC 與 ASP.NET Web Form 可以和平共處的原因之一,因為 ASP.NET Web Form 一定會有個 aspx 檔案 (或其他檔案類型),當 HTTP 要求進來後,就會立即找到有個實體檔案在,這時就會直接跳過 ASP.NET Routing 機制。
這樣的設計其實在大部分的環境下都可以正常運作,但在一些特殊的設計裡,你可能會想要求所有 HTTP 要求都必須經過 ASP.NET Routing 機制的比對才行 (例如你想藉由 ASP.NET Routing 實做權限管理),這時就可以在 Global.asax.cs 檔案裡的 RegisterRoutes 方法加上以下程式碼即可:
routes.RouteExistingFiles = true;
如下圖示:

這樣的設計當然也會帶來一些副作用,因為當你真的有實體檔案要能直接透過 IIS 回應的時候,就必須針對特定的檔案類型或路徑設定 IgnoreRoute 或 StopRoutingHandler (本文技巧 3 有提到)。
ASP.NET MVC 開發心得分享 (21):Routing 觀念與技巧的更多相关文章
- Delphi APP 開發入門(九)拍照與分享
Delphi APP 開發入門(九)拍照與分享 分享: Share on facebookShare on twitterShare on google_plusone_share 閲讀次數:30 ...
- ASP.NET MVC TempData使用心得
说明: 在ASP.NET MVC中資料傳遞主要有ViewData與TempData ViewData主要是Controller傳遞Data給View,存留期只有一個Action,要跨Action要使用 ...
- Delphi APP 開發入門(七)通知與雲端推播
Delphi APP 開發入門(七)通知與雲端推播 分享: Share on facebookShare on twitterShare on google_plusone_share 閲讀次數: ...
- 自己用的一个ASP.Net MVC分页拿出来分享下(转)
实例懒得做.切几个图把代码发上要用的自己搞啦~ 下面是一个helper类. namespace System.Web.Mvc { public enum BarStyle { yahoo, digg, ...
- [ASP.NET MVC 小牛之路]07 - URL Routing
我们知道在ASP.NET Web Forms中,一个URL请求往往对应一个aspx页面,一个aspx页面就是一个物理文件,它包含对请求的处理. 而在ASP.NET MVC中,一个URL请求是由对应的一 ...
- ASP.NET MVC 小牛之旅3:Routing——网址路由
网址路由(Routing)在ASP.NET MVC中有两个主要用途,一个用途是匹配通过浏览器传来的HTTP请求,另一个用途则是响应适当的网址给浏览器. 3.1匹配通过浏览器传来的HTTP请求 首先我们 ...
- IIS 发布ASP.NET MVC 4.0 错误500.21解决办法
由VS2013 写好的MVC 4.0 发布在服务器IIS 上报错500.21,解决办法:尝试多种网上介绍的办法,最后发现还是模块问题.
- 【ASP.NET MVC 学习笔记】- 08 URL Routing
本文参考:http://www.cnblogs.com/willick/p/3343105.html 1.URL Routing告诉MVC如何正确的定位Controller和Action. 2.URL ...
- ASP.NET MVC5写.php路由匹配时的问题 ASP.NET MVC 4 在 .NET 4.0 与.NET 4.5 的專案範本差異
由于外包公司结束合作,所以考虑把其APP服务替换过来,因原后台是用php写的,在不影响员客户端使用的情况下在MVC下重写路由配置实现处理原php链接地址的请求,但实现时发现怎么也匹配不到自己写的路由, ...
随机推荐
- Javascript中的迭代、归并方法
迭代方法 在Javascript中迭代方法个人觉得尤为重要,在很多时候都会有实际上的需求,javascript提供了5个迭代方法来供我们操作,它们分别为: every() 对数组中的每一个项运用给定的 ...
- Mac OS X平台上Java环境的配置
最近换了工作,以前是做c/c++的,但是现在公司的主打产品是使用Java开发,为了以后维护代码,现在开始抽空学习一下Java相关的内容. 在学习之前,首先需要搭建各种平台的开发环境,而我选用的操作系统 ...
- Windows Linux HackMacintosh
我想把Windows Linux HackMacintosh三类系统融入到一台笔记本上的神经病应该不多. 我的电脑就一个SATA硬盘,BIOS还不是EFI的.一共同时安装了Windows 8.1.Op ...
- java basic
//java 声明常量 //final 数据类型 常量名=值; //as: final float PI=3.14f;/ PI=3.14002F //默认浮点为 double //break:跳出多重 ...
- jQuery.ajax()的一些例子
例子: Example: 保存数据到服务器,成功时显示信息. 1 2 3 4 5 6 7 $.ajax({ method: "POST", url: "some.php& ...
- 自制3D打印机---挤出头
计划准备自己制作一台3D打印机,故将制作过程记录在此方便以后查阅. 计划首先制作加热头部件,此部件的主要功能是通过加热棒加热挤出头,然后从送料管道将ABS或者PLA材料线材送入后融化成为液体后,从挤出 ...
- wpf image控件循环显示图片 以达到动画效果 问题及解决方案
1>最初方案: 用wpf的image控件循环显示图片,达到动画效果,其实就是在后台代码动态改变Image.Source的值,关键代码: ; i < ; i++)//六百张图片 { Bitm ...
- Word分栏
情景描述 Word分栏在小论文的撰写过程中是很常用的技术.但是,我们经常会遇到很难过的情况: 一段文字本来是连续分布的,可是当选择了分两栏 之后,开始部分在左边一栏,中间在右边一栏. ...
- css 垂直同步的几种方式
第一种: 利用 display:table 和 display:table-cell 的方式 这种方式就好像将table布局搬过来一样,相信大家对table的td还是有印象的,它的内容是可以设置垂直居 ...
- ubuntu下的文本查看相关命令
文本查看 1.cat命令(查看文本内容) 使用时三种常用模式 (1)cat 文本名 直接查看文本内容 (2)cat 文本名 -n 直接查看文本内容,但为文本中所有行编号 (3)cat 文本名 -b 直 ...