精通ASP.NET MVC ——路由
本文章將關注定義路由,并使用它們去處理URL,使用戶能夠到達控制器和動作。
文章非常長,可以對路由機制有較初步的了解。首先創建示例項目,項目名為UrlAndRoutes,如下圖所示:
?
然后是創建示例控制器和示例視圖,有三個控制器,分別為Admin控制器,Home控制器,Customer控制器,一個命名為ActionName示例視圖。這三個控制器都返回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視圖的代碼很簡單,就是返回調用它的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>測試示例代碼,啟動默認路由,顯示頁面沒有問題,如下圖所示:
? ? ? ? ? ? ???
?
URL模式簡介?
路由系統由一組路由來實現它的功能。這些路由共同組成了應用程序的URL架構。這種URL架構是應用程序能夠識別并能對之做出響應的一組URL。
URL可以分成幾個片段。除主機名和查詢字符串之外,這些URL的組成部分都用"/"字符進行分割,如下所示:
第一個片段含有單詞“Admin”,第二個片段含有單詞“Index”。很顯然,第一個片段和控制器有關,第二個片段和動作有關。以下是做這件事的一個URL模式:{ controller } / { action }?
當處理一個輸入請求時,路由系統的工作是將這個請求URL與一個模式進行匹配,然后從此URL為這個模式中定義的片段變量提取出相應的值。片段變量用花括號 “ { ” 和 “ } ”字符表示。上述示例模式有兩個片段變量,其名稱分別為“controller”和“action”,因此,controller片段的值是Admin,而action片段的值是Index。
所謂“與一個模式”匹配是指,一個MVC應用程序通常會有幾條路由,而路由系統會把輸入URL逐一與每條路由的URL模式相比較,直到能找到一條匹配的路由為止。
默認情況下,一個URL模式將匹配具有正確片段的的任何URL。例如,模式{controller} / {action}將匹配任何具有兩個片段的URL,如下圖所示:
| 請求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模式的兩個關鍵行為:
1、URL模式是保守的,因此只匹配與模式具有相同片段的URL。你可以從表中第四個,第五個例子看到這種情況(片段數不同就是不匹配)。
2、URL模式是寬松的,如果一個URL正好是具有正確的片段數,該模式就用來為片段變量提取值,而不管這個值是什么。
?
?創建并注冊一條簡單路由
路由是在RouteConfig.cs文件中進行定義的,該文件位于項目的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方法中其他代碼,以便更加關注重點,如下圖所示:
namespace UrlsAndRoutes {public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}");}} }運行代碼,可以看到如下報錯界面:?
但是如果導航到一個與{controller} / {action }?匹配的URL時,將看到正確顯示。如下圖,就導航到了/Home/Index:
? ? ? ? ?
?
定義默認值?
當請求應用程序的默認值時,出現錯誤的原因是它不匹配已經定義的路由。前面說過,URL是保守的,他們只匹配指定片段的URL。改變這種行為的一個方式是使用默認值。修改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片段提供了一個默認值,該路由也將匹配單片段URL。當處理單片段URL時,路由系統將從唯一的URL片段中提取controller的值,并對action變量使用默認值。于是,可以請求http://localhost:29802/home?,系統會自動調用home控制器上的Index動作方法。運行效果如下圖所示:
? ? ? ? ? ? ?
當然,也可以更直接一點,對controller也賦默認值,代碼如下圖所示:?
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"});}} }這就是為什么剛新建了一個項目,沒有任何片段 就導航到home控制器下的Index動作的原因,因為RegisterRoute方法里默認控制器是home,動作是Index,運行效果如下圖所示:
? ? ? ? ? ? ??
?使用靜態URL片段
?并不是一個URL模式中的所有片段都需要是可變的。也可以創建具有靜態片段的模式。假設希望匹配一下這種URL,以支持帶有public前綴的URL:http://localhost:29802/Public/Home/Index?可以通過修改如下代碼來實現這個效果:
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"});}}這個全新的URL模式將只匹配含有三個片段的URL,第一個必須是public,其他兩個片段含有任何值,并將被用于controller和action變量。如果省略后兩個片段,那么將使用默認值。?運行效果如下圖所示:
? ? ? ? ? ? ? ? ? ? ?
?
?還可以創建既有靜態變量也有可變元素片段的URL模式,如下圖代碼所示:
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"});}第一條路由的模式是匹配任意兩個片段URL,而第一個片段是以“X”打頭。用于controller的值取自第一個片段除“X”以外的部分,Action的值取自第二個片段。如果啟動程序并到導航到? /XHome/Index ,可以看到如下圖所示:
注意:路由系統根據最先定義的路由模式來匹配一個輸入URL,并且只有在不匹配的時候,才會繼續對下一條路由進行處理。路由依次被嘗試,直到找出匹配的一條,或這組路由被嘗試完。所以,必須首先定義教具體的路由。如果將上圖中兩條路由改變定義的順序,那么X{controller}這條路由將永遠無法到達,路由系統回去找 名為“XHome”?的控制器,因為這個控制器是不存在的,所以會報404——未找到的錯誤。
可以結合靜態變量片段和默認值為特定的路由創建一個別名。如果已經公開發布了URL方案,而且它與你的用戶形成了一種契約,那么,創建這種別名可能是有用的。如果在這種情況下(指已經于用戶形成契約)重構應用程序,則需要保留以前的URL格式。設想以前用的是一個Shop控制器,現在要有Home控制器來代替。修改代碼,下圖演示了如何才能創建一個保留舊式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"});}添加這條路由匹配第一個片段是“Shop”的任意兩片段URL,action的值取自第二個URL片段。這個URL模式不含controller的可變片段,因而會使用所提供的默認值。這意味著對Shop控制器上一個動作請求會被轉換成Home控制器的請求,啟動程序,并導航到/Shop/Index 網址。如下圖所示:
而且可以更進一步,被重構且不再出現在控制器中的動作方法創建別名,為此,只要簡單的創建一個靜態URL,并提供controller和action的默認值,如下圖所示:
{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"});}再一次提醒,要注意放置新路由的位置,以使它被首先定義。這是因為新路由比后面的路由更具體。例如,如果一個對Shop/OldAction 的請求被下一條路由定義來處理,就會得到與想要的不同的結果。這個請求將被處理成一個“404——未找到”錯誤,并注意,路由名稱必須唯一。運行結果,如下圖所示:
? ? ? ? ? ? ? ? ??
定義自定義片段變量?
contorller和action片段變量對MVC框架而言有特殊的含義,顯然,它們對應于請求進行服務的控制器和動作方法。?但是也可以自己定義變量。修改代碼如下圖所示:
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模式定義了標準的controller 和 action 變量,以及一個名為“id”的自定義變量。這條路由將匹配任何0~3個片段的URL。第三個片段的內容將被賦值給id變量,而且如果沒有第三個片段,將采用默認值。?
通過使用RouteData.Values屬性,能夠在一個動作方法中訪問任何一個片段變量。如下圖所示,對Home控制器添加了以一個名為“CustomVariable”的動作方法:
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>運行程序,并導航到 /Home/CustomVariable/Hello 網址,結果如下圖所示:
? ? ? ??
? ? ? ? ? 以為給變量id提供了一個默認值DefaultId,所以導航到? /Home/CustomVariable 網址,結果如下圖所示:
? ? ? ? ?
用自定義變量作為動作方法參數?
使用RouteData.Values屬性只是訪問自定義路由變量的一種方式。另一種方式要優雅的多。如果以URL模式中的變量相匹配的名稱,來定義動作方法的參數,MVC框架將把從URL獲得的值作為參數傳遞給該動作方法。?代碼如下圖所示:
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();}} }當路由系統根據上圖所定義的路由來匹配一個URL時,URL中第三個片段的值被賦值給了自定義變量id。MVC框架會將片段變量列表與動作方法參數列表進行比較,如果名稱匹配,便將URL的值傳遞給該方法。?
?這里將id參數定義成一個string,但MVC框架會嘗試將URL的值轉化為所定義的任何參數類型,如果將id參數聲明為int 或者 DateTime,那么,從URL模式接收到的將是一個被解析成該類型示例的值。
?
定義可選URL片段
?可選URL片段是指,用戶不需要指定,但又未指定默認值的片段。通過將默認值設置為“UrlParameter.Optional”,便指明了一個片段變量是可選的,如下圖所示:
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,下表演示了它對不同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 | 不匹配,片段太多 |
由上表可見,只有當輸入URL中存在相應片段時,id變量才會被添加到變量集合中。?在下圖代碼中,對控制器做了修改,以相應無對應值的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();}} }運行效果如下圖所示:?
使用可選URL片段強制關注分離
如果開發人員十分注重MVC模式中的關注分離,他們不喜歡將片段變量的默認值放在應用程序的路由中。如果是因為這樣,可以使用C#中的可選參數,以及路由中的可選片段變量,來定義動作方法的參數的默認值。?
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id = "DefaultId")//可選參數{ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id;return View();}}以上代碼和下面的路由是等價的:?
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = "DefaultId" });定義可變長路由
改變URL模式默認保守性的另一種方式是接收可變數目的URL片段。這讓你能夠以一個單一的路由,對任意長度的URL進行路由。通過指定一個叫做“全匹配(catch)”的片段變量,并以星號(*)作為其前綴,便可以定義對可變片段數的支持。代碼如下圖所示:
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 });}}?現在,這條路由將匹配任何URL,無論URL包含多少個片段數,也不管這些片段的值是什么。前三個片段分別用于設置controller、action和id變量的值。如果URL含有更多的片段,則它們全部被賦給catchall 變量。如下圖所示:
| 片段數 | 示例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模式所匹配的片段數量是沒有上限的。注意:有catchall捕獲的片段是以“片段/片段/片段”的形式表示的。你要對這個字符串進行處理,把它分解為一個個片段。?
按命名空間區分控制器優先順序
當一個輸入請求URL與一條路由進行匹配時,MVC框架取得controller變量的值,并查找相應的控制器的名稱。例如,當controller變量名稱是“Home”時,那么,MVC框架會查找名稱為“HomeController”的控制器。這是一個不合格的類名,如果兩個或者多個名為“HomeController”的控制器,MVC框架將不知道怎么做。
為了測試,在項目的根目錄下創建一個名為AdditionalControllers的文件夾,并添加一個Home控制器,如下圖所示:
namespace UrlsAndRoutes.AdditionalControllers {public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Additional Controllers --- Home";ViewBag.Action = "Index";return View("ActionName");}} }運行程序,會發現報了如下錯誤:?
很明顯可以看到是錯誤提示是控制器重復了,這個問題比想象的更會經常出現,尤其是在一些大的MVC項目中,?遇到命名沖突只是時間問題。為了解決這一問題,可以告訴MVC框架,在試圖解析控制器名稱的時候,對某些命名空間給予優先處理,如下圖所示:
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" });}} }上述代碼中把命名空間表示成一個字符串數組,告訴MVC框架,在考察其他命名空間之前,先考察UrlsAndRoutes.AdditionalControllers 命名空間。
如果在這個命名空間中找不到合適控制器,那么MVC框架會默認回到正常行為,并考察所有可用的命名空間。?運行結果如下圖所示:
? ? ? ? ? ? ? ?
注意:添加到一條路由的命名空間具有同等的優先級,MVC框架不會先檢查第一命名空間,然后第二、第三命名空間。也就是說,同一個條路由中的命名空間不是按順序進行檢查的,而是會被同等對待的。如下圖中,如果把兩個項目的命名空間都加到這條路由里來,還是會報錯:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional}, new[] { "UrlsAndRoutes.AdditionalControllers" , "UrlsAndRoutes.Controllers" });如果希望對一個命名空間的某個控制器給與優先級,但是又要解析另一個命名空間中的所有其他控制器,就需要創建多條路由,如下圖所示:
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"});當用戶明確的請求一個片段為Home的URL時,會運用第一條路由,并且會以AdditionalControllers文件夾中的Home控制器為目標。所有的其他請求,包括未指定第一片段的那些請求,會以controllers文件夾中的控制器去處理。?
也可以告訴MVC框架,只考察指定的命名空間。如果找不到一個匹配的控制器,那么框架不會搜索其他地方。代碼如下所示:
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;//禁止搜索其他命名空間?
用則表達式約束路由?
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"});}}?以上代碼用正則表達式形成了一個約束,它只匹配controller變量值已“C”字母的大頭的URL。
默認值是是在約束檢查之前運用的,因此,如果請求的URL是“/”,會將以默認值“Home”運用于controller,然后才會檢查約束。而且,如果此時controller的值是以“C”,則會報錯,如下圖所示:
如果把約束改成以H開頭,則運行正常:?
? ? ? ? ? ??
將一條路由約束到一組指定的值?
可以用正則表達式來約束一條路由,以便對于一個URL片段,只有指定的一些值才能形成匹配。可以用豎線“|”字符來做這件事。代碼如下圖所示:
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")},//限制請求類型 new[] { "UrlsAndRoutes.Controllers"});上述代碼將這條路由限制到GET和POST請求。?
使用類型和值約束?
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#類型值,并執行基本的檢查。在上圖中,使用了RangeRouteConstraint類,它檢查提供給片段的變量的值是在兩個邊界之間的一個有效的int類型。下圖描述了完整的約束集合。他們并不接受參數,由于它們將用于配置路由,所以僅顯示類名,暫且忽略了屬性約束列。
| 名稱 | 描述 | 屬性約束 |
| AlphaRouteConstraint() | 匹配字母字符 主要是(A~Z,a~z) | alpha |
| BoolRouteConstraint() | 匹配一個可以解析成bool類型的值 | bool |
| DateTimeRouteConstraint() | 匹配一個可以解析成DateTime類型的值 | datetime |
| DecimalRouteConstraint() | 匹配一個可以解析成deciaml類型的值 | decimal |
| DoubleRouteConstraint() | 匹配一個可以解析成double類型的值 | double |
| FloatRouteConstraint() | 匹配一個可以解析成float類型的值 | float |
| IntRouteConstraint() | 匹配一個可以解析成int類型的值 | int |
| LengthRouteConstraint(len) LengthRouteConstraint(min,max) | 匹配一個指定字符個數的值,或匹配字符個數在min和max之間的值 | length(len)? ? ?length(min,max) |
| LongRouteConstraint() | 匹配一個可以解析成long類型的值 | long |
| MaxRouteConstraint(val) | 匹配一個值小于val的int值 | max(val) |
| MaxLengthRouteConstraint(len) | 匹配一個長度不超過len的字符值 | maxlength(len) |
| MinRouteConstraint(val) | 匹配一個值大于val的int值 | min(val) |
| MinLengthRouteConstraint(len) | 匹配一個長度至少為len字符串 | minlength(len) |
| RangeRouteConstraint(min,max) | 匹配一個值在min和max之間的值 | range(min,max) |
?使用CompoundRouteConstraint類,該類接受一個約束數組作為它構造器的參數,可以為一個單一的片段變量組合不同的約束。如下圖所示,同時將AlphaRouteConstraint 和?MinLengthRouteConstraint運用到id片段變量,以確保路由將僅匹配包含字母字符并且至少含有6個字符的字符串值:
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"});定義自定義約束?
?如果標準約束不滿足你的需求,可以通過實現IRouteConstraint接口,來定義自己的自定義約束。在示例項目中添加一個Infrastructrue文件夾,并創建新的類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方法,它的實現可以用來對路由系統指示它的約束是否已經得到滿足。Match方法的參數提供了對以下對象的訪問:客戶端請求,待評估路由,約束的參數名,從URL提取的片段變量,以及該請求要檢查的是輸入URL還是輸入的URL的細節。對于上述示例,要檢查的是客戶端請求的UserAgent屬性的值,看它是否含有一個被傳遞給構造器的值。
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"});第一條路由使他之匹配來自用戶代理字符串含有Chrome的瀏覽器的請求,并指向UrlsAndRoutes.AdditionalControllers。該路由是有一個片段,意味著controller 和 action 總是取自默認值,而不是URL本身。?
第二條路由將匹配其他所有請求,并以controller文件夾中的控制器為目標,這兩條路由的情況是,有一種瀏覽器最終只能訪問程序的同一個位置。第二條路由動用了約束,第三個片段只能包含6個或者以上的字母字符,以使第二個路由匹配。運行結果如下圖所示:
? ? ?
???
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的精通ASP.NET MVC ——路由的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 题解P1613跑路
- 下一篇: C#中IEnumerableT.Sele