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 程式碼,這也是初學者很容易看不懂的地方。

以下文字用來描述上圖標號的部分:

  1. ASP.NET 執行的起點就在於 HttpApplication 的 Application_Start() 方法,所有 Routing 都會定義在此,其中 RouteTable.Routes 是一個公開靜態物件,用來儲存所有的 Routing 規則,其物件型別為 RouteCollection
  2. 在預設 RegisterRoutes 方法中的 IgnoreRoute 用來定義 不要透過 Routing 處理的網址。 
    註: IgnoreRoute 擴充方法是 ASP.NET MVC (System.Web.Mvc) 的一部份。
  3. {resource} 代表一個 路由變數(RouteValue),其名稱為 resource,但在這裡其實取任何名字都可以,這裡只代表一個變數空間 (PlaceHolder) 罷了。總之就是代表一個「位置」,可以放入一個用不到的變數。
  4. {*pathInfo} 也是代表一個 RouteValue 名稱為 pathInfo,但名稱前面的星號 ( * ) 代表 CatchAll的意思,這個名為 pathInfo 的 RouteValue 會是完整的 PATH INFO 扣除 標號 3 比對到的網址。例如:若網址是 /TEST.axd/a/b/c/d 則 pathInfo 所得到的值為 a/b/c/d,如果沒加上星號 ( * ) 該 pathInfo 就會等於 a 而已。而在這裡其實取任何名字都可以,這裡也只是代表一個變數的位置。
  5. MapRoute 則是最常用來定義 Routing 規則的擴充方法。 
    註: MapRoute 擴充方法是 ASP.NET MVC (System.Web.Mvc) 的一部份。
  6. 定義 Route 名稱,這個名稱雖然少用,但是開發到較為進階的 Routing 技巧時會用到。
  7. 定義 網址格式 與每個 網址段落(Path Segment) 的 RouteValue 參數名稱。 
    注意: 該網址不能以斜線 ( / ) 開頭。
  8. 定義各 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 規則:

技巧 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 有提到)。

时间: 2024-10-11 20:15:21

ASP.NET MVC 開發心得分享 (21):Routing 觀念與技巧的相关文章

Delphi APP 開發入門(九)拍照與分享

Delphi APP 開發入門(九)拍照與分享 分享: Share on facebookShare on twitterShare on google_plusone_share 閲讀次數:3022 發表時間:2014/07/01 tags: 行動開發 教學 App Delphi XE6 Android iOS Delphi APP 開發入門(八)SQLite資料庫 << 前情 在眾多行動APP中,除了遊戲類之外,熱門的APP不外乎都會與拍照.分享.地圖等相關的技術有關聯,今天筆者就與朋友們

ASP.NET MVC TempData使用心得

说明: 在ASP.NET MVC中資料傳遞主要有ViewData與TempData ViewData主要是Controller傳遞Data給View,存留期只有一個Action,要跨Action要使用TempData, 而TempData依TempDataProvider的不同,會有不同的存留期,預設的TempDataProvider是SessionStateTempDataProvider, 你沒有看錯,預設是用Session來存放TempData, Session不是使用者存放資料,而且存留

Delphi APP 開發入門(七)通知與雲端推播

Delphi APP 開發入門(七)通知與雲端推播 分享: Share on facebookShare on twitterShare on google_plusone_share 閲讀次數:5131 發表時間:2014/06/18 tags: 行動開發 教學 App Delphi XE6 Android iOS Delphi APP 開發入門(六)Object Pascal 語法初探 << 前情 今天教大家使用的是通知(Notification)功能,在Delphi XE6很貼心的也將通

ASP.NET MVC 小牛之旅3:Routing——网址路由

网址路由(Routing)在ASP.NET MVC中有两个主要用途,一个用途是匹配通过浏览器传来的HTTP请求,另一个用途则是响应适当的网址给浏览器. 3.1匹配通过浏览器传来的HTTP请求 首先我们来看下第一个用途,也就是匹配通过浏览器传来的HTTP请求. 客户端对ASP.NET网站发出请求时,能够通过Routing找到合适的HttpHandler来处理网页,大致的处理流程为: 如果HttpHandler是由MvcHandler来处理的,那么,此时就会进入ASP.NET MVC的执行生命周期,

[FPGA] DP83848網路晶片開發心得(1)

最近想要用FPGA來存取網路,於是想到之前買的一個DP88848晶片的網路版子,剛好拿出來研究研究. 查了一下規格它是使用RMII的傳輸介面可以達到10M/100M的速度,應該很夠用了吧. 那第一步就是先研究一下這電路要怎麼接?剛好有在網路找到其電路圖? 買了 一個洞洞板,接到DE0-Nano的GPIO_0,腳位如下: pin discrption GPIO_0[10]: MDC    GPIO_0[11]: MDIO GPIO_0[12]: CRS     GPIO_0[13]: OSCIN

Delphi APP 開發入門(六)Object Pascal 語法初探

Delphi APP 開發入門(六)Object Pascal 語法初探 分享: Share on facebookShare on twitterShare on google_plusone_share 閲讀次數:3442 發表時間:2014/06/10 tags: 行動開發 教學 App Delphi XE6 Android iOS Delphi APP 開發入門(五)GPS 定位功能 << 前情 經過前面五週幾乎每週可以寫出一個簡單App後,大家都可以感受到Delphi強大的開發威力!

Delphi APP 開發入門(八)SQLite資料庫

Delphi APP 開發入門(八)SQLite資料庫 分享: Share on facebookShare on twitterShare on google_plusone_share 閲讀次數:5279 發表時間:2014/06/25 tags: 行動開發 教學 App Delphi XE6 Android iOS SQLite Delphi APP 開發入門(七)通知與雲端推播 << 前情 在撰寫APP程式時,如果遇到資料間無關聯性且資料量不多時,會以像xml檔案去儲存,但一旦遇到關聯

ASP.NET MVC

[ASP.NET MVC 大牛之路]03 - C#高级知识点概要(2) - 线程和并发 摘要: 我也想过跳过C#高级知识点概要直接讲MVC,但经过前思后想,还是觉得有必要讲的.我希望通过自己的经验给大家一些指引,带着大家一起走上ASP.NET MVC大牛之路,少走弯路.同时也希望能和大家一起交流,这样也能发现我自己的不足,对我自己的帮助也是非常大的.建议大家对C#撑握的不错的时候,可以去看一些...阅读全文 posted @ 2014-12-29 23:15 Liam Wang 阅读(9730)

[ASP.NET MVC 小牛之路]18 - Web API

原文:[ASP.NET MVC 小牛之路]18 - Web API Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API.Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的一部分,微软把Web API相关的类从 System.Web.Mvc 命名空间下提取了出来放在 System.Web.Http 命名空间下.这种理念是把 Web API 作为ASP.NET 平台的核心之一,以使Web API能使用在