精通ASP.NET MVC ——路由
本文章將關(guān)注定義路由,并使用它們?nèi)ヌ幚鞺RL,使用戶能夠到達(dá)控制器和動(dòng)作。
文章非常長(zhǎng),可以對(duì)路由機(jī)制有較初步的了解。首先創(chuàng)建示例項(xiàng)目,項(xiàng)目名為UrlAndRoutes,如下圖所示:
?
然后是創(chuàng)建示例控制器和示例視圖,有三個(gè)控制器,分別為Admin控制器,Home控制器,Customer控制器,一個(gè)命名為ActionName示例視圖。這三個(gè)控制器都返回ActionName視圖。代碼如下圖所示:
namespace UrlsAndRoutes.Controllers {public class AdminController : Controller{// GET: Adminpublic ActionResult Index(){ViewBag.Controller = "Admin";ViewBag.Action = "Index";return View("ActionName");}} }?
namespace UrlsAndRoutes.Controllers {public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}} } namespace UrlsAndRoutes.Controllers {public class CustomerController : Controller{// GET: Customerpublic ActionResult Index(){ViewBag.Controller = "Customer";ViewBag.Action = "Index";return View("ActionName");}public ActionResult List(){ViewBag.Controller = "Customer";ViewBag.Action = "List";return View("ActionName");}} }ActionName.cshtml視圖的代碼很簡(jiǎn)單,就是返回調(diào)用它的control和ActionName的名稱,如下圖所示:
@{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>ActionName</title> </head> <body><div> the control is @ViewBag.Controller</div><div> the action is @ViewBag.Action</div> </body> </html>測(cè)試示例代碼,啟動(dòng)默認(rèn)路由,顯示頁(yè)面沒(méi)有問(wèn)題,如下圖所示:
? ? ? ? ? ? ???
?
URL模式簡(jiǎn)介?
路由系統(tǒng)由一組路由來(lái)實(shí)現(xiàn)它的功能。這些路由共同組成了應(yīng)用程序的URL架構(gòu)。這種URL架構(gòu)是應(yīng)用程序能夠識(shí)別并能對(duì)之做出響應(yīng)的一組URL。
URL可以分成幾個(gè)片段。除主機(jī)名和查詢字符串之外,這些URL的組成部分都用"/"字符進(jìn)行分割,如下所示:
第一個(gè)片段含有單詞“Admin”,第二個(gè)片段含有單詞“Index”。很顯然,第一個(gè)片段和控制器有關(guān),第二個(gè)片段和動(dòng)作有關(guān)。以下是做這件事的一個(gè)URL模式:{ controller } / { action }?
當(dāng)處理一個(gè)輸入請(qǐng)求時(shí),路由系統(tǒng)的工作是將這個(gè)請(qǐng)求URL與一個(gè)模式進(jìn)行匹配,然后從此URL為這個(gè)模式中定義的片段變量提取出相應(yīng)的值。片段變量用花括號(hào) “ { ” 和 “ } ”字符表示。上述示例模式有兩個(gè)片段變量,其名稱分別為“controller”和“action”,因此,controller片段的值是Admin,而action片段的值是Index。
所謂“與一個(gè)模式”匹配是指,一個(gè)MVC應(yīng)用程序通常會(huì)有幾條路由,而路由系統(tǒng)會(huì)把輸入U(xiǎn)RL逐一與每條路由的URL模式相比較,直到能找到一條匹配的路由為止。
默認(rèn)情況下,一個(gè)URL模式將匹配具有正確片段的的任何URL。例如,模式{controller} / {action}將匹配任何具有兩個(gè)片段的URL,如下圖所示:
| 請(qǐng)求URL | 片段變量 |
| http://mysite.com/Admin/Index | controller = Admin? action = Index |
| http://mysite.com/Index/Admin | controller = Index? action = Admin |
| http://mysite.com/Apples/Orange | controller = Apples action = Orange |
| http://mystie.com/Admin | 不匹配,片段太少 |
| http://mysite/Admin/Index/Soccer | 不匹配,片段太多 |
?
上圖突出了URL模式的兩個(gè)關(guān)鍵行為:
1、URL模式是保守的,因此只匹配與模式具有相同片段的URL。你可以從表中第四個(gè),第五個(gè)例子看到這種情況(片段數(shù)不同就是不匹配)。
2、URL模式是寬松的,如果一個(gè)URL正好是具有正確的片段數(shù),該模式就用來(lái)為片段變量提取值,而不管這個(gè)值是什么。
?
?創(chuàng)建并注冊(cè)一條簡(jiǎn)單路由
路由是在RouteConfig.cs文件中進(jìn)行定義的,該文件位于項(xiàng)目的App_start文件夾中。?代碼如下圖所示:
namespace UrlsAndRoutes {public class 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 });}} }先刪掉上圖中RegisterRoutes方法中其他代碼,以便更加關(guān)注重點(diǎn),如下圖所示:
namespace UrlsAndRoutes {public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}");}} }運(yùn)行代碼,可以看到如下報(bào)錯(cuò)界面:?
但是如果導(dǎo)航到一個(gè)與{controller} / {action }?匹配的URL時(shí),將看到正確顯示。如下圖,就導(dǎo)航到了/Home/Index:
? ? ? ? ?
?
定義默認(rèn)值?
當(dāng)請(qǐng)求應(yīng)用程序的默認(rèn)值時(shí),出現(xiàn)錯(cuò)誤的原因是它不匹配已經(jīng)定義的路由。前面說(shuō)過(guò),URL是保守的,他們只匹配指定片段的URL。改變這種行為的一個(gè)方式是使用默認(rèn)值。修改RegisterRoutes方法如下圖所示:
namespace UrlsAndRoutes {public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}",new { action = "Index"});}} }上述代碼中為action片段提供了一個(gè)默認(rèn)值,該路由也將匹配單片段URL。當(dāng)處理單片段URL時(shí),路由系統(tǒng)將從唯一的URL片段中提取controller的值,并對(duì)action變量使用默認(rèn)值。于是,可以請(qǐng)求http://localhost:29802/home?,系統(tǒng)會(huì)自動(dòng)調(diào)用home控制器上的Index動(dòng)作方法。運(yùn)行效果如下圖所示:
? ? ? ? ? ? ?
當(dāng)然,也可以更直接一點(diǎn),對(duì)controller也賦默認(rèn)值,代碼如下圖所示:?
namespace UrlsAndRoutes {public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}",new {controller = "home", action = "Index"});}} }這就是為什么剛新建了一個(gè)項(xiàng)目,沒(méi)有任何片段 就導(dǎo)航到home控制器下的Index動(dòng)作的原因,因?yàn)镽egisterRoute方法里默認(rèn)控制器是home,動(dòng)作是Index,運(yùn)行效果如下圖所示:
? ? ? ? ? ? ??
?使用靜態(tài)URL片段
?并不是一個(gè)URL模式中的所有片段都需要是可變的。也可以創(chuàng)建具有靜態(tài)片段的模式。假設(shè)希望匹配一下這種URL,以支持帶有public前綴的URL:http://localhost:29802/Public/Home/Index?可以通過(guò)修改如下代碼來(lái)實(shí)現(xiàn)這個(gè)效果:
public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","public/{controller}/{action}",new {controller = "home", action = "Index"});}}這個(gè)全新的URL模式將只匹配含有三個(gè)片段的URL,第一個(gè)必須是public,其他兩個(gè)片段含有任何值,并將被用于controller和action變量。如果省略后兩個(gè)片段,那么將使用默認(rèn)值。?運(yùn)行效果如下圖所示:
? ? ? ? ? ? ? ? ? ? ?
?
?還可以創(chuàng)建既有靜態(tài)變量也有可變?cè)仄蔚腢RL模式,如下圖代碼所示:
public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("", "X{controller}/{action}");//第一條路由routes.MapRoute("MyRoute","public/{controller}/{action}",//第二條路由new {controller = "home", action = "Index"});}第一條路由的模式是匹配任意兩個(gè)片段URL,而第一個(gè)片段是以“X”打頭。用于controller的值取自第一個(gè)片段除“X”以外的部分,Action的值取自第二個(gè)片段。如果啟動(dòng)程序并到導(dǎo)航到? /XHome/Index ,可以看到如下圖所示:
注意:路由系統(tǒng)根據(jù)最先定義的路由模式來(lái)匹配一個(gè)輸入U(xiǎn)RL,并且只有在不匹配的時(shí)候,才會(huì)繼續(xù)對(duì)下一條路由進(jìn)行處理。路由依次被嘗試,直到找出匹配的一條,或這組路由被嘗試完。所以,必須首先定義教具體的路由。如果將上圖中兩條路由改變定義的順序,那么X{controller}這條路由將永遠(yuǎn)無(wú)法到達(dá),路由系統(tǒng)回去找 名為“XHome”?的控制器,因?yàn)檫@個(gè)控制器是不存在的,所以會(huì)報(bào)404——未找到的錯(cuò)誤。
可以結(jié)合靜態(tài)變量片段和默認(rèn)值為特定的路由創(chuàng)建一個(gè)別名。如果已經(jīng)公開(kāi)發(fā)布了URL方案,而且它與你的用戶形成了一種契約,那么,創(chuàng)建這種別名可能是有用的。如果在這種情況下(指已經(jīng)于用戶形成契約)重構(gòu)應(yīng)用程序,則需要保留以前的URL格式。設(shè)想以前用的是一個(gè)Shop控制器,現(xiàn)在要有Home控制器來(lái)代替。修改代碼,下圖演示了如何才能創(chuàng)建一個(gè)保留舊式URL方案的路由。
public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("ShopSchem","Shop/{action}", //新增路由new { controller = "Home" });routes.MapRoute("", "X{controller}/{action}");routes.MapRoute("MyRoute","public/{controller}/{action}",new {controller = "home", action = "Index"});}添加這條路由匹配第一個(gè)片段是“Shop”的任意兩片段URL,action的值取自第二個(gè)URL片段。這個(gè)URL模式不含controller的可變片段,因而會(huì)使用所提供的默認(rèn)值。這意味著對(duì)Shop控制器上一個(gè)動(dòng)作請(qǐng)求會(huì)被轉(zhuǎn)換成Home控制器的請(qǐng)求,啟動(dòng)程序,并導(dǎo)航到/Shop/Index 網(wǎng)址。如下圖所示:
而且可以更進(jìn)一步,被重構(gòu)且不再出現(xiàn)在控制器中的動(dòng)作方法創(chuàng)建別名,為此,只要簡(jiǎn)單的創(chuàng)建一個(gè)靜態(tài)URL,并提供controller和action的默認(rèn)值,如下圖所示:
{routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("ShopSchem2", "Shop/OldAction", //新增路由new { controller = "Home",action = "Index"});routes.MapRoute("ShopSchem","Shop/{action}", new { controller = "Home" });routes.MapRoute("", "X{controller}/{action}");routes.MapRoute("MyRoute","public/{controller}/{action}",new {controller = "home", action = "Index"});}再一次提醒,要注意放置新路由的位置,以使它被首先定義。這是因?yàn)樾侣酚杀群竺娴穆酚筛唧w。例如,如果一個(gè)對(duì)Shop/OldAction 的請(qǐng)求被下一條路由定義來(lái)處理,就會(huì)得到與想要的不同的結(jié)果。這個(gè)請(qǐng)求將被處理成一個(gè)“404——未找到”錯(cuò)誤,并注意,路由名稱必須唯一。運(yùn)行結(jié)果,如下圖所示:
? ? ? ? ? ? ? ? ??
定義自定義片段變量?
contorller和action片段變量對(duì)MVC框架而言有特殊的含義,顯然,它們對(duì)應(yīng)于請(qǐng)求進(jìn)行服務(wù)的控制器和動(dòng)作方法。?但是也可以自己定義變量。修改代碼如下圖所示:
public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home",action = "Index",id = "DefaultId"});}該路由的URL模式定義了標(biāo)準(zhǔn)的controller 和 action 變量,以及一個(gè)名為“id”的自定義變量。這條路由將匹配任何0~3個(gè)片段的URL。第三個(gè)片段的內(nèi)容將被賦值給id變量,而且如果沒(méi)有第三個(gè)片段,將采用默認(rèn)值。?
通過(guò)使用RouteData.Values屬性,能夠在一個(gè)動(dòng)作方法中訪問(wèn)任何一個(gè)片段變量。如下圖所示,對(duì)Home控制器添加了以一個(gè)名為“CustomVariable”的動(dòng)作方法:
namespace UrlsAndRoutes.Controllers {public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = RouteData.Values["id"];return View();}} }并添加CustomVariable.cshtml視圖與之匹配,代碼如下圖所示:?
@{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>CustomVariable</title> </head> <body><div> The controller is : @ViewBag.Controller</div><div> The action is @ViewBag.Action</div><div> The custom varibale is @ViewBag.CustomVariable</div> </body> </html>運(yùn)行程序,并導(dǎo)航到 /Home/CustomVariable/Hello 網(wǎng)址,結(jié)果如下圖所示:
? ? ? ??
? ? ? ? ? 以為給變量id提供了一個(gè)默認(rèn)值DefaultId,所以導(dǎo)航到? /Home/CustomVariable 網(wǎng)址,結(jié)果如下圖所示:
? ? ? ? ?
用自定義變量作為動(dòng)作方法參數(shù)?
使用RouteData.Values屬性只是訪問(wèn)自定義路由變量的一種方式。另一種方式要優(yōu)雅的多。如果以URL模式中的變量相匹配的名稱,來(lái)定義動(dòng)作方法的參數(shù),MVC框架將把從URL獲得的值作為參數(shù)傳遞給該動(dòng)作方法。?代碼如下圖所示:
namespace UrlsAndRoutes.Controllers {public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id;return View();}} }當(dāng)路由系統(tǒng)根據(jù)上圖所定義的路由來(lái)匹配一個(gè)URL時(shí),URL中第三個(gè)片段的值被賦值給了自定義變量id。MVC框架會(huì)將片段變量列表與動(dòng)作方法參數(shù)列表進(jìn)行比較,如果名稱匹配,便將URL的值傳遞給該方法。?
?這里將id參數(shù)定義成一個(gè)string,但MVC框架會(huì)嘗試將URL的值轉(zhuǎn)化為所定義的任何參數(shù)類型,如果將id參數(shù)聲明為int 或者 DateTime,那么,從URL模式接收到的將是一個(gè)被解析成該類型示例的值。
?
定義可選URL片段
?可選URL片段是指,用戶不需要指定,但又未指定默認(rèn)值的片段。通過(guò)將默認(rèn)值設(shè)置為“UrlParameter.Optional”,便指明了一個(gè)片段變量是可選的,如下圖所示:
public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = UrlParameter.Optional});}}以上的路由將匹配不管是否提供id的URL,下表演示了它對(duì)不同URL的工作方式:?
| 片段 | 示例URL | 映射成 |
| 0 | mydomain.com | controller = Home? action = Index |
| 1 | mydomain.com/Customer | controller = Customer action = Index |
| 2 | mydomain.com/Customer/List | controller = Customer action = List |
| 3 | mydomain.com/Customer/List/All | controller = Customer action = List id = All |
| 4 | mydomain.com/Customer/List/All/Delete | 不匹配,片段太多 |
由上表可見(jiàn),只有當(dāng)輸入U(xiǎn)RL中存在相應(yīng)片段時(shí),id變量才會(huì)被添加到變量集合中。?在下圖代碼中,對(duì)控制器做了修改,以相應(yīng)無(wú)對(duì)應(yīng)值的id片段變量。
namespace UrlsAndRoutes.Controllers {public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id ?? "<no value>";return View();}} }運(yùn)行效果如下圖所示:?
使用可選URL片段強(qiáng)制關(guān)注分離
如果開(kāi)發(fā)人員十分注重MVC模式中的關(guān)注分離,他們不喜歡將片段變量的默認(rèn)值放在應(yīng)用程序的路由中。如果是因?yàn)檫@樣,可以使用C#中的可選參數(shù),以及路由中的可選片段變量,來(lái)定義動(dòng)作方法的參數(shù)的默認(rèn)值。?
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id = "DefaultId")//可選參數(shù){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id;return View();}}以上代碼和下面的路由是等價(jià)的:?
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = "DefaultId" });定義可變長(zhǎng)路由
改變URL模式默認(rèn)保守性的另一種方式是接收可變數(shù)目的URL片段。這讓你能夠以一個(gè)單一的路由,對(duì)任意長(zhǎng)度的URL進(jìn)行路由。通過(guò)指定一個(gè)叫做“全匹配(catch)”的片段變量,并以星號(hào)(*)作為其前綴,便可以定義對(duì)可變片段數(shù)的支持。代碼如下圖所示:
public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index", id = UrlParameter.Optional });}}?現(xiàn)在,這條路由將匹配任何URL,無(wú)論URL包含多少個(gè)片段數(shù),也不管這些片段的值是什么。前三個(gè)片段分別用于設(shè)置controller、action和id變量的值。如果URL含有更多的片段,則它們?nèi)勘毁x給catchall 變量。如下圖所示:
| 片段數(shù) | 示例URL | 映射成 |
| 0 | / | controller = Home action = Index |
| 1 | /Customer | controller = Customer action = Index |
| 2 | /Customer/List | controller = Customer action = List |
| 3 | /Customer/List/All | controller = Customer action = List id = All |
| 4 | /Customer/List/All/Delete | controller = Customer action = List id = All catchall = Delete |
| 5 | /Customer/List/All/Delete/Perm | controller = Customer action = List id = All catchall = Delete/Perm |
以上路由中的URL模式所匹配的片段數(shù)量是沒(méi)有上限的。注意:有catchall捕獲的片段是以“片段/片段/片段”的形式表示的。你要對(duì)這個(gè)字符串進(jìn)行處理,把它分解為一個(gè)個(gè)片段。?
按命名空間區(qū)分控制器優(yōu)先順序
當(dāng)一個(gè)輸入請(qǐng)求URL與一條路由進(jìn)行匹配時(shí),MVC框架取得controller變量的值,并查找相應(yīng)的控制器的名稱。例如,當(dāng)controller變量名稱是“Home”時(shí),那么,MVC框架會(huì)查找名稱為“HomeController”的控制器。這是一個(gè)不合格的類名,如果兩個(gè)或者多個(gè)名為“HomeController”的控制器,MVC框架將不知道怎么做。
為了測(cè)試,在項(xiàng)目的根目錄下創(chuàng)建一個(gè)名為AdditionalControllers的文件夾,并添加一個(gè)Home控制器,如下圖所示:
namespace UrlsAndRoutes.AdditionalControllers {public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Additional Controllers --- Home";ViewBag.Action = "Index";return View("ActionName");}} }運(yùn)行程序,會(huì)發(fā)現(xiàn)報(bào)了如下錯(cuò)誤:?
很明顯可以看到是錯(cuò)誤提示是控制器重復(fù)了,這個(gè)問(wèn)題比想象的更會(huì)經(jīng)常出現(xiàn),尤其是在一些大的MVC項(xiàng)目中,?遇到命名沖突只是時(shí)間問(wèn)題。為了解決這一問(wèn)題,可以告訴MVC框架,在試圖解析控制器名稱的時(shí)候,對(duì)某些命名空間給予優(yōu)先處理,如下圖所示:
namespace UrlsAndRoutes {public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index", id = UrlParameter.Optional},new[] { "UrlsAndRoutes.AdditionalControllers" });}} }上述代碼中把命名空間表示成一個(gè)字符串?dāng)?shù)組,告訴MVC框架,在考察其他命名空間之前,先考察UrlsAndRoutes.AdditionalControllers 命名空間。
如果在這個(gè)命名空間中找不到合適控制器,那么MVC框架會(huì)默認(rèn)回到正常行為,并考察所有可用的命名空間。?運(yùn)行結(jié)果如下圖所示:
? ? ? ? ? ? ? ?
注意:添加到一條路由的命名空間具有同等的優(yōu)先級(jí),MVC框架不會(huì)先檢查第一命名空間,然后第二、第三命名空間。也就是說(shuō),同一個(gè)條路由中的命名空間不是按順序進(jìn)行檢查的,而是會(huì)被同等對(duì)待的。如下圖中,如果把兩個(gè)項(xiàng)目的命名空間都加到這條路由里來(lái),還是會(huì)報(bào)錯(cuò):
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new[] { "UrlsAndRoutes.AdditionalControllers" , "UrlsAndRoutes.Controllers" });如果希望對(duì)一個(gè)命名空間的某個(gè)控制器給與優(yōu)先級(jí),但是又要解析另一個(gè)命名空間中的所有其他控制器,就需要?jiǎng)?chuàng)建多條路由,如下圖所示:
routes.MapRoute("AddControllerRoute", "Home/{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, new[] { "UrlsAndRoutes.AdditionalControllers" });routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new[] { "UrlsAndRoutes.Controllers"});當(dāng)用戶明確的請(qǐng)求一個(gè)片段為Home的URL時(shí),會(huì)運(yùn)用第一條路由,并且會(huì)以AdditionalControllers文件夾中的Home控制器為目標(biāo)。所有的其他請(qǐng)求,包括未指定第一片段的那些請(qǐng)求,會(huì)以controllers文件夾中的控制器去處理。?
也可以告訴MVC框架,只考察指定的命名空間。如果找不到一個(gè)匹配的控制器,那么框架不會(huì)搜索其他地方。代碼如下所示:
Route myRoute = routes.MapRoute("AddControllerRoute", "Home/{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, new[] { "UrlsAndRoutes.AdditionalControllers" });myRoute.DataTokens["UseNamespaecFallback"] = false;//禁止搜索其他命名空間?
用則表達(dá)式約束路由?
public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index", id = UrlParameter.Optional},new { controller = "^C.*"},new[] { "UrlsAndRoutes.Controllers"});}}?以上代碼用正則表達(dá)式形成了一個(gè)約束,它只匹配controller變量值已“C”字母的大頭的URL。
默認(rèn)值是是在約束檢查之前運(yùn)用的,因此,如果請(qǐng)求的URL是“/”,會(huì)將以默認(rèn)值“Home”運(yùn)用于controller,然后才會(huì)檢查約束。而且,如果此時(shí)controller的值是以“C”,則會(huì)報(bào)錯(cuò),如下圖所示:
如果把約束改成以H開(kāi)頭,則運(yùn)行正常:?
? ? ? ? ? ??
將一條路由約束到一組指定的值?
可以用正則表達(dá)式來(lái)約束一條路由,以便對(duì)于一個(gè)URL片段,只有指定的一些值才能形成匹配??梢杂秘Q線“|”字符來(lái)做這件事。代碼如下圖所示:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new { controller = "^H.*",action = "^Index$|^About$"}, new[] { "UrlsAndRoutes.Controllers"});上面這條約束將允許這條路由值匹配 action 片段的值是 “Index” 或 “About”的URL,controller的值必須是H打頭。?
?
?使用HTTP方法約束路由
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new { controller = "^H.*",action = "^Index$|^About$", httpMethod = new HttpMethodConstraint("GET","POST")},//限制請(qǐng)求類型 new[] { "UrlsAndRoutes.Controllers"});上述代碼將這條路由限制到GET和POST請(qǐng)求。?
使用類型和值約束?
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new { controller = "^H.*",action = "^Index$|^About$", httpMethod = new HttpMethodConstraint("GET","POST"), id = new RangeRouteConstraint(10,20)},//值約束 new[] { "UrlsAndRoutes.Controllers"});在System.Web.Mvc.Routing.Constraints 命名空間的約束類中,檢查片段變量是否是不同的C#類型值,并執(zhí)行基本的檢查。在上圖中,使用了RangeRouteConstraint類,它檢查提供給片段的變量的值是在兩個(gè)邊界之間的一個(gè)有效的int類型。下圖描述了完整的約束集合。他們并不接受參數(shù),由于它們將用于配置路由,所以僅顯示類名,暫且忽略了屬性約束列。
| 名稱 | 描述 | 屬性約束 |
| AlphaRouteConstraint() | 匹配字母字符 主要是(A~Z,a~z) | alpha |
| BoolRouteConstraint() | 匹配一個(gè)可以解析成bool類型的值 | bool |
| DateTimeRouteConstraint() | 匹配一個(gè)可以解析成DateTime類型的值 | datetime |
| DecimalRouteConstraint() | 匹配一個(gè)可以解析成deciaml類型的值 | decimal |
| DoubleRouteConstraint() | 匹配一個(gè)可以解析成double類型的值 | double |
| FloatRouteConstraint() | 匹配一個(gè)可以解析成float類型的值 | float |
| IntRouteConstraint() | 匹配一個(gè)可以解析成int類型的值 | int |
| LengthRouteConstraint(len) LengthRouteConstraint(min,max) | 匹配一個(gè)指定字符個(gè)數(shù)的值,或匹配字符個(gè)數(shù)在min和max之間的值 | length(len)? ? ?length(min,max) |
| LongRouteConstraint() | 匹配一個(gè)可以解析成long類型的值 | long |
| MaxRouteConstraint(val) | 匹配一個(gè)值小于val的int值 | max(val) |
| MaxLengthRouteConstraint(len) | 匹配一個(gè)長(zhǎng)度不超過(guò)len的字符值 | maxlength(len) |
| MinRouteConstraint(val) | 匹配一個(gè)值大于val的int值 | min(val) |
| MinLengthRouteConstraint(len) | 匹配一個(gè)長(zhǎng)度至少為len字符串 | minlength(len) |
| RangeRouteConstraint(min,max) | 匹配一個(gè)值在min和max之間的值 | range(min,max) |
?使用CompoundRouteConstraint類,該類接受一個(gè)約束數(shù)組作為它構(gòu)造器的參數(shù),可以為一個(gè)單一的片段變量組合不同的約束。如下圖所示,同時(shí)將AlphaRouteConstraint 和?MinLengthRouteConstraint運(yùn)用到id片段變量,以確保路由將僅匹配包含字母字符并且至少含有6個(gè)字符的字符串值:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new { controller = "^H.*",action = "^Index$|^About$", httpMethod = new HttpMethodConstraint("GET","POST"), id = new CompoundRouteConstraint( new IRouteConstraint[] {new AlphaRouteConstraint(), new MinLengthRouteConstraint(6) })}, new[] { "UrlsAndRoutes.Controllers"});定義自定義約束?
?如果標(biāo)準(zhǔn)約束不滿足你的需求,可以通過(guò)實(shí)現(xiàn)IRouteConstraint接口,來(lái)定義自己的自定義約束。在示例項(xiàng)目中添加一個(gè)Infrastructrue文件夾,并創(chuàng)建新的類UserAgentConstrainst.cs,如下所示:
namespace UrlsAndRoutes.Infrastructure {public class UserAgentConstraint:IRouteConstraint{private string requiredUserAgent;public UserAgentConstraint(string agentParam){this.requiredUserAgent = agentParam;}public bool Match(HttpContextBase httpContext,Route route,String parameterName,RouteValueDictionary values,RouteDirection routeDirection){return httpContext.Request.UserAgent != null && httpContext.Request.UserAgent.Contains(requiredUserAgent);}} }IRouteConstraint 接口定義了?Match方法,它的實(shí)現(xiàn)可以用來(lái)對(duì)路由系統(tǒng)指示它的約束是否已經(jīng)得到滿足。Match方法的參數(shù)提供了對(duì)以下對(duì)象的訪問(wèn):客戶端請(qǐng)求,待評(píng)估路由,約束的參數(shù)名,從URL提取的片段變量,以及該請(qǐng)求要檢查的是輸入U(xiǎn)RL還是輸入的URL的細(xì)節(jié)。對(duì)于上述示例,要檢查的是客戶端請(qǐng)求的UserAgent屬性的值,看它是否含有一個(gè)被傳遞給構(gòu)造器的值。
namespace UrlsAndRoutes.Infrastructure {public class UserAgentConstraint:IRouteConstraint{private string requiredUserAgent;public UserAgentConstraint(string agentParam){this.requiredUserAgent = agentParam;}public bool Match(HttpContextBase httpContext,Route route,String parameterName,RouteValueDictionary values,RouteDirection routeDirection){return httpContext.Request.UserAgent != null && httpContext.Request.UserAgent.Contains(requiredUserAgent);}} }并增加一條路由:
routes.MapRoute("ChromeRoute", "{*catchall}", new { controller = "Home", action = "Index" }, new { customConstraint = new UserAgentConstraint("Chrome") }, new[] { "UrlsAndRoutes.AdditionalControllers" });routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new { controller = "^H.*",action = "^Index$|^About$", httpMethod = new HttpMethodConstraint("GET"), id = new CompoundRouteConstraint( new IRouteConstraint[] { new AlphaRouteConstraint(), new MinLengthRouteConstraint(6) })}, new[] { "UrlsAndRoutes.Controllers"});第一條路由使他之匹配來(lái)自用戶代理字符串含有Chrome的瀏覽器的請(qǐng)求,并指向UrlsAndRoutes.AdditionalControllers。該路由是有一個(gè)片段,意味著controller 和 action 總是取自默認(rèn)值,而不是URL本身。?
第二條路由將匹配其他所有請(qǐng)求,并以controller文件夾中的控制器為目標(biāo),這兩條路由的情況是,有一種瀏覽器最終只能訪問(wèn)程序的同一個(gè)位置。第二條路由動(dòng)用了約束,第三個(gè)片段只能包含6個(gè)或者以上的字母字符,以使第二個(gè)路由匹配。運(yùn)行結(jié)果如下圖所示:
? ? ?
???
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的精通ASP.NET MVC ——路由的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 题解P1613跑路
- 下一篇: 精通ASP.NET MVC ——控制器可