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链接地址的请求,但实现时发现怎么也匹配不到自己写的路由, ...
随机推荐
- ECMAScript布尔操作符
在ECMAScript中提供了Boolean()转换函数以及三个布尔操作符,这三个布尔操作符分别为逻辑非.逻辑与.逻辑或,这三个操作符通常用作于某些值的求反,比较模式等.学好这一点知识也非常的重要,奠 ...
- 我们都忽略了Html5的力量,如果只看成一种技术就大错特错了!
第一部分:Html5市场的力量 我们太忽略Html5的市场力量了.如果你把Html5当作一种技术,就大错特错了!忘掉你的产品,忘掉你的技术,想想移动时代的信息传播和消费场景.作为2B,我们实在是没有重 ...
- JS判断输入框值是否为空
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- Python设计模式——设计原则
1.单一职责原则:每个类都只有一个职责,修改一个类的理由只有一个 2.开放-封闭远程(OCP):开放是指可拓展性好,封闭是指一旦一个类写好了,就尽量不要修改里面的代码,通过拓展(继承,重写等)来使旧的 ...
- 如何根据w3wp.exe的进程pid查看是哪个应用程序池
如何根据w3wp.exe的进程pid查看是哪个应用程序池? 根据iisapp 查看PID所对应的IIS应用程序池及详细介绍: 从IIS6.0可以在IIS中架设多个站点并给每个站点指定不同的应用程序池, ...
- WPF中Application.Current的使用
WPF程序对应一个Application对象,当前的Application对象可以通过Application.Current获取,通过获取到的Application对象,我们可以做以下的事情: App ...
- poj 2001 Shortest Prefixes trie入门
Shortest Prefixes 题意:输入不超过1000个字符串,每个字符串为小写字母,长度不超过20:之后输出每个字符串可以简写的最短前缀串: Sample Input carbohydrate ...
- SD卡FAT32文件系统格式
一.声明 1.本文来源和主旨 2.本文测试环境 二.SD卡FAT文件系统 1.SD卡FAT32文件系统的整体布局 2.FAT文件系统简介 ① 文件分配表 ② 目录项 三.DBR(DOS BOOT RE ...
- linux下使用split 来分割大文件
linux下使用split 来分割大文件 2010-07-27 15:46:27| 分类: 技术文稿 | 标签:split 分割 linux |字号 订阅 平常都是使用ssh来进行远程 ...
- 关于怎样拆毁Cuttheprice
最近我的chrome浏览器被一个叫cuttheprice的插件病毒感染了,总是弹出广告,真是让人抓狂,本来最近几天在复习算法,准备面试的,昨天忍了过去,今天实在受不了了,于是就干掉它吧.说干就干,刚开 ...