天天看點

(二) 識别URL的Routing元件(mvc)

這個系列我感覺寫得很好。入門最佳

轉自:

http://www.cnblogs.com/zhangziqiu/archive/2009/02/28/ASPNET-MVC-2.html

一.摘要

本篇文章從基礎到深入的介紹ASP.NET MVC中的Routing元件. Routing翻譯過來是"路由選擇", 負責ASP.NET MVC的第一個工作:識别URL, 将一個Url請求"路由"給Controller.

二.承上啟下

第一篇文章中我們已經學會了如何使用ASP.NET MVC, 雖然其中還有很多的細節沒有深入了解, 但是對基本的處理流程已經有了認識:來了一個Url請求, 從中找到Controller和Action的值, 将請求傳遞給Controller處理. Controller擷取Model資料對象, 并且将Model傳遞給View, 最後View負責呈現頁面.

而Routing的作用就是負責分析Url, 從Url中識别參數, 如圖:

(二) 識别URL的Routing元件(mvc)

這一講就讓我們細緻的了解System.Web.Routing及其相關的擴充知識.

三.Routing的作用

第一講中執行個體的首頁位址是: localhost/home/index

我們發現通路上面的位址, 最後會傳遞給 HomeController中名為index的action(即HomeController類中的index方法).

當然伺服器端不會自己去實作這個功能,  關鍵點就是在Global.asax.cs檔案中的下列代碼:

public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults ); } protected void Application_Start() { RegisterRoutes(RouteTable.Routes); }      

回來看我們的Url: localhost/home/index

localhost是域名, 是以首先要去掉域名部分: home/index

對應了上面代碼中的這種URL結構: {controller}/{action}/{id}

因為我們建立了這種Url結構的識别規則, 是以能夠識别出 Controller是home, action是index, id沒有則為預設值"".

這就是Routing的第一個作用:

1.從Url中識别出資料.比如controller,action和各種參數.

如果跟蹤程式, 接下來我們會跳轉到HomeController中的Index()方法.  這是Routing内部為實作的第二個作用:

2.根據識别出來的資料, 将請求傳遞給Controller和Action.

但從執行個體中我們并不知道Routing如何做的這部份工作.第五部分我做了深入講解.

四.Routing的使用

在分析Routing的實作原理前, 先學習如何使用Routing為ASP.NET MVC程式添加路由規則.

1. 使用MapRoute()方法.

這是最簡單的為ASP.NET MVC添加識别規則的方法.此方法有如下重載:

MapRoute( string name, string url); MapRoute( string name, string url, object defaults); MapRoute( string name, string url, string[] namespaces); MapRoute( string name, string url, object defaults, object constraints); MapRoute( string name, string url, object defaults, string[] namespaces); MapRoute( string name, string url, object defaults, object constraints, string[] namespaces);      

name參數:

規則名稱, 可以随意起名.當時不可以重名,否則會發生錯誤:

路由集合中已經存在名為“Default”的路由。路由名必須是唯一的。

url參數:

url擷取資料的規則, 這裡不是正規表達式,  将要識别的參數括起來即可, 比如: {controller}/{action}

最少隻需要傳遞name和url參數就可以建立一條Routing(路由)規則.比如執行個體中的規則完全可以改為:

routes.MapRoute( "Default", "{controller}/{action}");      

defaults參數:

url參數的預設值.如果一個url隻有controller: localhost/home/

而且我們隻建立了一條url擷取資料規則: {controller}/{action}

那麼這時就會為action參數設定defaults參數中規定的預設值. defaults參數是Object類型,是以可以傳遞一個匿名類型來初始化預設值:

new { controller = "Home", action = "Index" }      

執行個體中使用的是三個參數的MapRoute方法:

routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults );      

constraints參數:

用來限定每個參數的規則或Http請求的類型.constraints屬性是一個RouteValueDictionary對象,也就是一個字典表, 但是這個字典表的值可以有兩種:

用于定義正規表達式的字元串。正規表達式不區分大小寫。

一個用于實作 IRouteConstraint 接口且包含

Match

方法的對象。

通過使用正規表達式可以規定參數格式,比如controller參數隻能為4位數字:

new { controller = @"\d{4}"}      

通過第IRouteConstraint 接口目前可以限制請求的類型.因為System.Web.Routing中提供了HttpMethodConstraint類, 這個類實作了IRouteConstraint 接口. 我們可以通過為RouteValueDictionary字典對象添加鍵為"httpMethod", 值為一個HttpMethodConstraint對象來為路由規則添加HTTP 謂詞的限制, 比如限制一條路由規則隻能處理GET請求:

httpMethod = new HttpMethodConstraint( "GET", "POST" )

      

完整的代碼如下:

routes.MapRoute(
            "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" }, // Parameter defaults new { controller = @"\d{4}" , httpMethod = new HttpMethodConstraint( "GET", "POST" ) } );      

當然我們也可以在外部先建立一個RouteValueDictionary對象在作為MapRoute的參數傳入, 這隻是文法問題.

namespaces參數:

此參數對應Route.DataTokens屬性. 官方的解釋是:

擷取或設定傳遞到路由處理程式但未用于确定該路由是否比對 URL 模式的自定義值。

我目前不知道如何使用. 請高手指點

2.MapRoute方法執行個體

下面通過執行個體來應用MapRoute方法. 對于一個網站,為了SEO友好,一個網址的URL層次不要超過三層:

localhost/{頻道}/{具體網頁}

其中域名第一層, 頻道第二層, 那麼最後的網頁就隻剩下最後一層了. 如果使用預設執行個體中的"{controller}/{action}/{其他參數}"的形式會影響網站的SEO.

假設我們的網站結構如下:

(二) 識别URL的Routing元件(mvc)

下面以酒店頻道為例, 是我建立的Routing規則:

public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); #region 酒店頻道部分 // hotels/list-beijing-100,200-3 routes.MapRoute( "酒店清單頁", "hotels/{action}-{city}-{price}-{star}", new { controller = "Hotel", action = "list", city = "beijing", price="-1,-1", star="-1" }, new { city=@"[a-zA-Z]*",price=@"(\d)+\,(\d)+", star="[-1-5]"} ); //hotels/所有比對 routes.MapRoute( "酒店首頁", "hotels/{*values}", new { controller = "Hotel", action = "default", hotelid = "" } ); #endregion //網站首頁. routes.MapRoute( "網站首頁", "{*values}", new { controller = "Home", action = "index"} ); }      

實作的功能:

(1)通路 localhost/hotels/list-beijing-100,200-3 會通路酒店頻道的清單頁,并傳入查詢參數

(2)通路 localhost/hotels 下面的任何其他頁面位址, 都會跳轉到酒店首頁.

(3)通路 localhost 下面的任何位址, 如果未比對上面2條, 則跳轉到首頁.

簡單總結:

(1)Routing規則有順序(按照添加是的順序), 如果一個url比對了多個Routing規則, 則按照第一個比對的Routing規則執行.

(2)由于上面的規則, 要将具體頻道的具體頁面放在最上方, 将頻道首頁 和 網站首頁 放在最下方.

(3) {*values} 表示後面可以使任意的格式.

3.使用Route類

MapRoute方法雖然簡單, 但是他是本質也是通過建立Route類的執行個體, 為RouteCollection集合添加成員.

下載下傳最新版本的MSDN-Visual Studio 20008 SP1, 已經可以找到Route類的說明.

建立一個Route類執行個體,最關鍵的是為以下幾個屬性指派:

屬性名稱 說明 舉例
Constraints 擷取或設定為 URL 參數指定有效值的表達式的詞典。 {controller}/{action}/{id}
DataTokens new RouteValueDictionary { { "format", "short" } }
Defaults 擷取或設定要在 URL 不包含所有參數時使用的值。 new { controller = "Home", action = "Index", id = "" }
RouteHandler 擷取或設定處理路由請求的對象。 new MvcRouteHandler()
Url 擷取或設定路由的 URL 模式。 new { controller = @"[^\.]*" }

這些屬性除了RouteHandler以外, 其他的都對應MapRoute方法的參數.RouteHandler是實作了IRouteHandler接口的對象.關于此接口的作用在第五部分Routing深入解析中做講解.

五.Routing深入解析

對于一個一般開發人員來說, 上面的知識已經完全足夠你使用ASP.NET MVC時使用Routing了.

接下來的部分我将深入Routing的機制講解Routing的進階應用.但是因為是"進階應用", 加上這篇文章已經太長了, 再加上馬上今天就過去了, "每日一篇"的承諾一定要兌現的, 是以不會對所有細節進行講解. 或者也可以略過此部分.

Routing如何将請求傳遞給Controller?上面講解Routing作用的時候, 我們就分析出Routing會将請求傳遞給Controller, 但是Routing如何做的這部份工作我們卻看不到.關鍵在于MapRoute()這個方法封裝了具體的細節.

雖然MapRoute方法是RouteCollection對象的方法,但是卻被放置在System.Web.Mvc程式集中, 如果你的程式隻引用了System.Web.Routing, 那麼RouteCollection對象是不會有MapRoute方法的. 但是如果你同又引用了System.Web.Mvc, 則在mvc的dll中為RouteCollection對象添加了擴充方法:

public static void IgnoreRoute(this RouteCollection routes, string url); public static void IgnoreRoute(this RouteCollection routes, string url, object constraints); public static Route MapRoute(this RouteCollection routes, string name, string url); public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults); public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces); public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints); public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces); public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);      

RouteCollection是一個集合,他的每一項應該是一個Route對象. 但是我們使用MapRoute時并沒有建立這個對象, 這是因為當我們将MapRoute方法需要的參數傳入時, 在方法内部會根據參數建立一個Route對象:

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints) }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens = new RouteValueDictionary(); route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }      

上面就是MapRoute方法的實作, 至于在建立Route對象時第二個參數是一個MvcRouteHandler, 它是一個實作了IRouteHandler接口的類. IRouteHandler十分簡單隻有一個方法:

IHttpHandler GetHttpHandler(RequestContext requestContext);      

參數是一個RequestContext 類執行個體, 這個類的結構也很簡單:

public class RequestContext { public RequestContext(HttpContextBase httpContext, RouteData routeData); public HttpContextBase HttpContext { get; } public RouteData RouteData { get; } }      

其中的一個屬性RouteData就包含了Routing根據Url識别出來各種參數的值, 其中就有Controller和Action的值.

歸根結底, ASP.NET MVC最後還是使用HttpHandler處理請求. ASP.NET MVC定義了自己的實作了IHttpHandler接口的Handler:MvcHandler,  因為MvcRouteHandler的GetHttpHandler方法最後傳回的就是MvcHandler. 

MvcHandler的構造函數需要傳入RequestContext 對象, 也就是傳入了所有的所有需要的資料, 是以最後可以找到對應的Controller和Action, 已經各種參數.

六.測試Routing

因為一個Url會比對多個routing規則, 最後常常會遇到規則寫錯或者順序不對的問題.于是我們希望能夠看到Url比對Routing的結果.

其中最簡單的辦法就是使用RouteDebug輔助類. 這個類需要單獨下載下傳dll元件, 我将此元件的下載下傳放在了部落格園上:

http://files.cnblogs.com/zhangziqiu/RouteDebug-Binary.zip

解壓縮後是一個DLL檔案, 将這個DLL檔案添加到項目中并且添加引用.

使用方法很簡單, 隻需要在Application_Start方法中添加一句話:

RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);      

比如下面是我的示例中的代碼:

protected void Application_Start() { RegisterRoutes(RouteTable.Routes); RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes); }      

現在你通路任何URL, 都會出現RouteDebug頁面, 如下:

(二) 識别URL的Routing元件(mvc)

其中不僅有你的所有Routing規則, 還顯示了是否比對.并且按照順序列出. 還有識别的參數清單.

當你不想測試Routing規則的時候則注釋掉這一段, 即可回複跳轉到View對象上.

七.總結

本文講解了ASP.NET MVC中一個關鍵的元件:Routing的使用. System.Web.Routing在Framework3.5 SP1中已經內建, 也就是說雖然我們還沒有ASP.NET MVC的正式版, 但是Routing元件卻已經提早釋出了. 因為Routing是一個相對獨立的元件, 不僅能和ASP.NET MVC配額使用, 也可以用于任何需要URL路由的項目. 另外Routing的作用和Url重寫(Url Rewrite)是有差別的, 你會發現Routing和Url Rewrite相比其實很麻煩, 無論是添加規則還是傳遞參數.對UrlRewite感興趣的可以去尋找UrlRewrite.dll這個元件, 很簡單很強大, 有關兩者的異同以及如何使用UrlRewrite這裡不在多說了.

部落格園大道至簡

http://www.cnblogs.com/jams742003/

轉載請注明:部落格園

繼續閱讀