Routing深入詳解
首先,ASP.Net MVC項目是URL請求驅動的,為什么訪問localhost/home/index會傳遞給HomeController中名為index的action(即HomeController類中的index方法)?
下面,我們一一來看下。
1.1 Routing的作用
假如有一個請求:localhost/home/index,那么路由需要做的事情如下:
(1)確定Controller
(2)確定Action
(3)確定其他參數
(4)根據識別出來的數據,將請求傳遞給Controller和Action
1.2 神奇的路由規則
根據路由的作用,我們可以知道它是一個“指路人”,指示我們的請求應該到達哪個Controller中的Action。那么,它是根據什么規則來指路的呢?我們可以在App_Start文件夾中的RouteConfig類中找到這個神奇的規則是如何制定的。
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
(1)首先,第一句routes.IgnoreRoute代表對所有axd的資源訪問請求進行忽略,直接進行URL訪問;這里可以閱讀參考資料第(5)篇,了解其詳細含義,這里就不再贅述;
(2)然后,第二句開始使用MapRoute方法對整個網站定義了一個路由識別規則,這個規則的name是Default,url規則為:{controller}/{action}/{id}。例如我們要訪問的URL為:localhost/home/index,在這個URL中,localhost是域名, 所以首先要去掉域名部分: home/index,也是就對應了上面代碼中的這種URL結構: {controller}/{action}/{id}。正是因為我們建立了這種URL結構的識別規則,,所以能夠識別出 Controller是home, action是index, id沒有則為默認值""。
(3)在MapRoute方法中為所有URL請求定義了一個defaults默認值:controller為空則指向Home,action為空則指向Index,而id則是可選的,非必須要的。
這里,對於路由規則需要注意的有兩點:
(1)可以有多條路由規則;
(2)路由規則是有順序的(前面的規則被匹配后,后面的規則就不再匹配);
我們可以在RegisterRoutes這個方法中添加一條自定義路由規則,並取名為Default2,具體規則代碼如下:
routes.MapRoute( name: "Default2", url: "{controller}-{action}-{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
這下如果我們以:localhost/Home-Index來訪問時,我們原本想要的是根據Default2這個路由規則訪問Home控制器下的Index這個Action,但卻被告知以404提示:
這是為什么呢?我們再來看看RegisterRoutes這個方法:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); routes.MapRoute( name: "Default2", url: "{controller}-{action}-{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
我們剛剛提到,路由規則是有順序的(前面的規則被匹配后,后面的規則就不再匹配)。那么,可以推斷,由於Default2在Default之后,有可能我們的請求localhost/Home-Index已經被Default這個規則所匹配了,因此Default2規則根本沒有出場Show一下。那么,在Default規則中,它將Home-Index作為Controller的名字匹配,去訪問Home-Index這個Controller,而Action使用默認的Index,那么它所請求的應該是這個URL:/localhost/Home-Index/Index。由於網站中,並沒有Home-Index這個Controller,所以也就出現了剛剛那個404頁面。
1.3 MapRoute方法介紹
(1)MapRoute方法提供了以下幾種方式的重載:
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);
我們在上面所使用的便是第二種重載。
(2)MapRoute方法參數詳細介紹:
①name參數:
規則名稱, 可以隨意起名。不可以重名,否則會發生錯誤: “路由集合中已經存在名為“Default”的路由。路由名必須是唯一的”。
②url參數:
url獲取數據的規則,這里不是正則表達式,將要識別的參數括起來即可,比如: {controller}/{action}
最少只需要傳遞name和url參數就可以建立一條Routing(路由)規則,比如實例中的規則完全可以改為:
routes.MapRoute( "Default", "{controller}/{action}");
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
routes.MapRoute( name: "Default2", url: "{controller}-{action}-{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints: new { controller = @"\d{4}" } );
例如:通過第IRouteConstraint 接口可以限制請求的類型(是GET還是POST)。因為System.Web.Routing中提供了HttpMethodConstraint類,,這個類實現了IRouteConstraint 接口。
我們可以通過為RouteValueDictionary字典對象添加鍵為"httpMethod", 值為一個HttpMethodConstraint對象來為路由規則添加HTTP 謂詞的限制,比如限制一條路由規則只能處理GET請求:httpMethod = new HttpMethodConstraint( "GET" )
routes.MapRoute( name: "Default2", url: "{controller}-{action}-{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints: new { controller = @"\d{4}", httpMethod = new HttpMethodConstraint("GET") } );
1.4 URL路由實例詳解
一般來說,對於一個網站為了SEO友好,網址的URL層次最好不要超過三層:localhost/{頻道}/{具體網頁},其中域名第一層, 頻道第二層, 那么最后的網頁就只剩下最后一層了。如果使用默認實例中的“{controller}/{action}/{其他參數}"的形式則會影響網站的SEO。
假設我們有一個綜合型服務網站,其中有租房頻道、酒店頻道、KTV頻道、電影院頻道等等。我們應該怎樣來設計URL路由規則呢?
(1)首先,我們知道:可以有多條路由規則,但是路由規則是有順序的(前面的規則被匹配后,后面的規則就不再匹配);所以,我們可以定義多條路由規則,粒度細的模塊(比如:具體的酒店列表頁面)路由規則放最前面,粒度粗的模塊(比如:門戶網站的首頁)路由規則放在最后面。
(2)其次,根據模塊粒度划分層次結構,以粒度粗細排序為:網站首頁->頻道首頁->具體內容;
(3)最后,我們可以看一個具體的URL路由實例來分析一下:
// 酒店列表頁匹配 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]" } ); // 酒店頻道所有匹配 routes.MapRoute( "酒店首頁", "hotels/{*iiii}", new { controller = "Hotel", action = "default", hotelid = "" } ); // 網站首頁默認匹配 routes.MapRoute( "網站首頁", "{*values}", new { controller = "Home", action = "index" } );
(4)我們可以分析一下上面的路由規則所實現的功能:
①訪問 www.mywebsite.com/hotels/list-chengdu-100,200-3 會訪問酒店頻道的列表頁,並傳入查詢參數(price為100,200,star為3);
②訪問 www.mywebsite.com/hotels 下面的任何其他頁面地址,都會跳轉到酒店首頁;
③訪問 www.mywebsite.com 下面的任何地址,如果未匹配上面2條,則跳轉到首頁;
(5)根據上面的規則和實現的功能,我們可以做一個簡單的總結如下:
①Routing規則有順序(按照添加是的順序),如果一個url匹配了多個路由規則,則按照第一個匹配的路由規則執行。
②由於上面的規則,要將具體頻道的具體頁面放在最上方,將頻道首頁 和 網站首頁 放在最下方。
③{*values}表示后面可以使用任意的格式。
1.5 URL路由調試
在ASP.Net MVC中,默認是不允許對路由規則進行調試的。但是,我們可以通過使用RouteDebug來輔助進行調試。
(1)首先,我們下載RouteDebug.dll,MvcContrib.dll到我們的項目中,並添加對其的引用。
(2)其次,在Global.asax中的Application_Start方法中添加一句代碼:
RouteDeug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles);
RouteDeug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}
(3)最后,F5調試運行,我們請求localhost/Home-Index這個URL時,可以清楚地發現,系統將Home-Index匹配了第一條默認路由規則,也就是將Home-Index作為Controller的名稱進行匹配,這也就證明了為什么我們輸入這個請求不會匹配第二條Default2的路由規則出現剛剛那個404頁面了。