ASP.NET MVC5 新特性:Attribute路由使用詳解
1、什麽是Attribute路由?怎麽樣啟用Attribute路由?
微軟在 ASP.NET MVC5 中引入了一種新型路由:Attribute路由,顧名思義,Attribute路由是通過Attribute來定義路由。當然,MVC5也支持以前定義路由的方式,你可以在一個項目中混合使用這兩種方式來定義路由。
在以前的版本中我們通常在 RouteConfig.cs 文件中通過以下方式來定義路由:
routes.MapRoute(
name: "ProductPage",
url: "{productId}/{productTitle}",
defaults: new { controller = "Products", action = "Show" },
constraints: new { productId = "\\d+" }
);
在MVC5中,我們可以把路由定義和 Action 放在一起:
[Route("{productId:int}/{productTitle}")]
public ActionResult Show(int productId) { ... }
當然,首先得啟用Attribute路由,我們可以調用MapMvcAttributeRoutes方法來啟用Attribute路由:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
}
2、URL可選參數和默認值
我們可以使用問號“?”來標記一個可選參數,也可以對參數設定默認值:
public class BooksController : Controller
{
// 匹配: /books
// 匹配: /books/1430210079
// 問號表示 isbn 是可選的
[Route("books/{isbn?}")]
public ActionResult View(string isbn)
{
if (!String.IsNullOrEmpty(isbn))
{
return View("OneBook", GetBook(isbn));
}
return View("AllBooks", GetBooks());
}
// 匹配: /books/lang
// 匹配: /books/lang/en
// 匹配: /books/lang/he
// 如果URL不傳遞 lang 參數,則lang的值為“en”
[Route("books/lang/{lang=en}")]
public ActionResult ViewByLanguage(string lang)
{
return View("OneBook", GetBooksByLanguage(lang));
}
}
3、路由前綴
有時候在同一個 Controller 中,所有 Action 匹配的 URL 都擁有相同的前綴,如下:
public class ReviewsController : Controller
{
// 匹配: /reviews
[Route("reviews")]
public ActionResult Index() { ... }
// 匹配: /reviews/5
[Route("reviews/{reviewId}")]
public ActionResult Show(int reviewId) { ... }
// 匹配: /reviews/5/edit
[Route("reviews/{reviewId}/edit")]
public ActionResult Edit(int reviewId) { ... }
}
我們看到 ReviewsController 下的所有 Action 前面都帶有 "reviews",這時我們可以在 Controller 上使用 [RoutePrefix]設置路由前綴,為每個 Action 所匹配的 URL 加上共同的前綴 "reviews":
[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
// 匹配: /reviews
[Route]
public ActionResult Index() { ... }
// 匹配: /reviews/5
[Route("{reviewId}")]
public ActionResult Show(int reviewId) { ... }
// 匹配: /reviews/5/edit
[Route("{reviewId}/edit")]
public ActionResult Edit(int reviewId) { ... }
}
但是,如果某一個 Action 不想要這個前綴怎麽辦?當然有辦法,我們可以用波浪號“~”來去掉它:
[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
// 匹配: /spotlight-review
[Route("~/spotlight-review")]
public ActionResult ShowSpotlight() { ... }
...
}
4、默認路由
我們除了可以在 Action 上使用[Route]外,也可以用在 Controller 上,當[Route]用在 Controller 上時,它就定義了一個默認路由規則,它會對這個 Controller 下的所有 Action 起作用,除非某個 Action 上也應用了 [Route] 特性覆蓋了 Controller 上的[Route]。但要註意的是應用在 Controller 上的 [Route] 一定要加上 {action},否則會拋出“RouteData 必須包含名為‘action‘且值為非空字符串的項。”錯誤。應用在 Action 上的 [Route] 則不用加,因為 {action} 就是當前 Action。
[RoutePrefix("promotions")]
[Route("{action=index}")]
//上面定義了默認路由,並且{action}的默認值為"index",
//也就是說 URL 不包含 {action} 時,默認調用的 Action 是 Index。
public class ReviewsController : Controller
{
// 匹配: /promotions
public ActionResult Index() { ... }
// 匹配: /promotions/archive
public ActionResult Archive() { ... }
// 匹配: /promotions/new
public ActionResult New() { ... }
// 匹配: /promotions/edit/5
// 這裏覆蓋了默認路由規則
// 按照默認路由,這裏應該匹配:/promotions/editProduct?promoId=5
[Route("edit/{promoId:int}")]
public ActionResult EditProduct(int promoId) { ... }
}
5、路由約束
路由約束可以讓你指定參數的類型以及範圍等,格式為:{參數:約束},舉例如下:
// 匹配: /users/5
[Route("users/{id:int}"]
// 這裏約束了參數“id”必須為整數類型
public ActionResult GetUserById(int id) { ... }
下面是支持的路由約束列表:
-
alpha,必須為大小寫字母(a-z,A-Z),如:{x:alpha};
-
bool,必須為布爾值,如:{x:bool}
-
datetime,必須為DateTime(時間和日期)類型,如:{x:datetime}
-
decimal,必須為decimal類型,如:{x:decimal}
-
double,必須為64bit浮點數,如:{x:double}
-
float,必須為32bit浮點數,如:{x:float}
-
guid,必須為GUID,如:{x:guid}
-
int,必須為32bit整數,如:{x:int}
-
length,字符串長度必須為指定值或者在指定範圍內,如:{x:length(6)} {x:length(1,20)}
-
long,必須為64bit整數,如:{x:long}
-
max,小於等於指定值的整數,如:{x:max(10)}
-
maxlength,字符串長度小於等於指定值,如:{x:maxlength(10)}
-
min,大於等於指定值的整數整數,如:{x:min(10)}
-
minlength,字符串長度大於等於指定值,如:{x:minlength(10)}
-
range,必須是給定範圍內的整數,如:{x:range(10,50)}
-
regex,必須與正則表達式匹配,如:{x:(^\d{3}-\d{3}-\d{4}$)}
你可以在一個參數後面應用多個約束,用冒號分隔它們,如下:
// 匹配: /users/5
// 但是不匹配 /users/10000000000 因為id的值已經超過了int.MaxValue,
// 也不匹配 /users/0 因為後面有個min(1)約束,id 的值必須大於等於 1.
[Route("users/{id:int:min(1)}")]
public ActionResult GetUserById(int id) { ... }
值得註意的是加在可選參數上的約束,例如:
// 匹配: /greetings/bye
// 也匹配 /greetings 因為message是可選參數,
// 但是不匹配 /greetings/see-you-tomorrow 因為有maxlength(3)約束.
[Route("greetings/{message:maxlength(3)?}")]
public ActionResult Greet(string message) { ... }
6、自定義路由約束
我們可以通過實現 IRouteConstraint 接口來自定義路由約束。下面的例子展示如何自定義路由約束:
public class ValuesConstraint : IRouteConstraint
{
private readonly string[] validOptions;
public ValuesConstraint(string options)
{
validOptions = options.Split(‘|‘);
}
public bool Match(HttpContextBase httpContext, Route route,
string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
}
return false;
}
}
下面的代碼是顯示如何註冊自定義的路由約束
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
}
}
現在我們就可以在代碼中使用這些自定義的路由約束了:
public class TemperatureController : Controller
{
// 匹配 temp/celsius 以及 /temp/fahrenheit 但不匹配 /temp/kelvin
[Route("temp/{scale:values(celsius|fahrenheit)}")]
public ActionResult Show(string scale)
{
return Content("scale is " + scale);
}
}
7、路由名稱
你可以為路由規則指定一個名稱,以方便生成相應的URL,舉例如下:
[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }
你可以使用 Url.RouteUrl 來生成相應的 URL:
<a href="@Url.RouteUrl("mainmenu")">Main menu</a>
8、Area
ASP.Net MVC 的 Area 概念對組織大型Web應用程序很有幫助,在Attribute路由中當然少不了對它的支持,只要使用 [RouteArea],就可以把 Controller 歸屬到某一個 Area 下,這時你可以放心的刪除 Area 下的 AreaRegistration 類了:
[RouteArea("Admin")]
[RoutePrefix("menu")]
[Route("{action}")]
public class MenuController : Controller
{
// 匹配: /admin/menu/login
public ActionResult Login() { ... }
// 匹配: /admin/menu/show-options
[Route("show-options")]
public ActionResult Options() { ... }
// 匹配: /stats
[Route("~/stats")]
public ActionResult Stats() { ... }
}
現在你可以和以往的版本一樣使用 "Admin" Area,下面的代碼會生成 URL "/Admin/menu/show-options":
Url.Action("Options", "Menu", new { Area = "Admin" })
你還可以通過 AreaPrefix 來設置 Area 前綴,例如:
[RouteArea("BackOffice", AreaPrefix = "back-office")]
如果你同時使用 Attribute、AreaRegistration 類這兩種方式來註冊 Area 的話,你應該在註冊 Attribute 路由和傳統路由映射之間使用 AreaRegistration 註冊 Area,原因很簡單,路由註冊順序必須是從最精確的匹配規則開始再到普通的匹配規則,最後才是模糊的匹配規則,這樣就避免了在進行路由匹配時,過早的匹配了模糊規則,而相對精確的匹配起不到任何作用。下面的例子展示了這一點:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
from:http://blog.csdn.net/cauchy8389/article/details/22937189
ASP.NET MVC5 新特性:Attribute路由使用詳解