您的位置:首页 > 编程语言 > ASP

【翻译】ASP.NET MVC 5属性路由

2015-01-25 10:45 211 查看
原文链接:http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx#why-attribute-routing

最近在学习MVC相关的东西,今天刚好在msdn上看到了这样的一片文章,感觉不错,于是决定将它翻译出来和博友们一起分享下。我第一次发表文章,有不对的地方非常欢迎指出。

                                                      —— 写在前面

废话不多说了,咱们开始吧

路由是ASP.NET MVC 怎样去用一个URI去匹配一个Action 。MVC5 支持一个新类型的路由,叫做属性路由。就像这个名字所说的,属性路由用属性去定义路由。在我们的web 应用程序中,属性路由给了我们URI更多的控制权。

之前我们用的路由方式,叫做基本的约定路由,现在仍然完全支持。你也可以在同一个项目中结合两种技术。

这篇文章将覆盖ASP.NET MVC 5属性路由的基本特点以及怎样去使用

一、为什么需要属性路由

二、怎样去使用属性路由

三、可选的URI参数和默认路由

四、路由前缀

五、默认路由

六、路由约束以及自定义路由约束

七、给路由取个名字

八、区域(Areas)

一、为什么需要属性路由

举个例子吧,在社会上比较好的的电商网站可能有如下的路由:

{productId:int}/{productTitle}

映射到:
ProductsController.Show(int id)


{username}

映射到:
ProfilesController.Show(string username)


{username}/catalogs/{catalogId:int}/{catalogTitle}

映射到:
CatalogsController.Show(string username, int catalogId)
(不在太在意这些特殊的语法,我们将在之后讲解中会涉及到)


在之前版本的ASP.NET MVC中,路由规则将在RouteConfig.cs文件中
设置,它指出了真实的控制器和方法名,列如:


[code] routes.MapRoute(
name: "ProductPage",
url: "{productId}/{productTitle}",
defaults: new { controller = "Products", action = "Show" },
constraints: new { productId = "\\d+" }
);


属性路由可以是我们更容易的去映射一个URI到一个Acion,上面的用属性路由我们就可以这样写:

[Route("{productId:int}/{productTitle}")]
public ActionResult Show(int productId) { ... }


二、怎样去使用属性路由

要去使用属性路由,必须先在到配置中去调用
MapMvcAttributeRoutes


public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapMvcAttributeRoutes();
}
}


我们也可以结合属性路由去对路由做一个基本的约束

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapMvcAttributeRoutes();

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}


三、可选的URI参数和默认值

你可以通过增加一个 "?" 标记到路由参数上去使用一个可选的URI参数。可以通过 parameter=value指定一个默认的值

public class BooksController : Controller
{
// eg: /books
// eg: /books/1430210079
[Route("books/{isbn?}")]
public ActionResult View(string isbn)
{
if (!String.IsNullOrEmpty(isbn))
{
return View("OneBook", GetBook(isbn));
}
return View("AllBooks", GetBooks());
}

// eg: /books/lang
// eg: /books/lang/en
// eg: /books/lang/he
[Route("books/lang/{lang=en}")]
public ActionResult ViewByLanguage(string lang)
{
return View("OneBook", GetBooksByLanguage(lang));
}
}


在这个例子中/books 和/books/1430210079都可以去访问"View",前者是去返回所有的Book,而后者是返回指定的Book。

四、路由前缀

通常在一个控制器中,都以相同的前缀开始,例如:

public class ReviewsController : Controller
{
// eg: /reviews
[Route("reviews")]
public ActionResult Index() { ... }
// eg: /reviews/5
[Route("reviews/{reviewId}")]
public ActionResult Show(int reviewId) { ... }
// eg: /reviews/5/edit
[Route("reviews/{reviewId}/edit")]
public ActionResult Edit(int reviewId) { ... }
}


这时我们就可以通过[RoutePrefix]属性去为整个控制器设置一个共同的前缀

[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
// eg.: /reviews
[Route]
public ActionResult Index() { ... }
// eg.: /reviews/5
[Route("{reviewId}")]
public ActionResult Show(int reviewId) { ... }
// eg.: /reviews/5/edit
[Route("{reviewId}/edit")]
public ActionResult Edit(int reviewId) { ... }
}


如果需要的话,你也可在Acion上用 “~" 去重写路由属性

[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
// eg.: /spotlight-review
[Route("~/spotlight-review")]
public ActionResult ShowSpotlight() { ... }

...
}


五、默认路由

我们也可以在一个Controller上去使用[Route],这个路由将被用在这个控制器所有的action上,除非一个指定的路由[Route]已经被用在一个action上(也就是重写了在这个Action的默认路由)。

[RoutePrefix("promotions")]
[Route("{action=index}")]
public class ReviewsController : Controller
{
// eg.: /promotions
public ActionResult Index() { ... }

// eg.: /promotions/archive
public ActionResult Archive() { ... }

// eg.: /promotions/new
public ActionResult New() { ... }

// eg.: /promotions/edit/5
[Route("edit/{promoId:int}")]
public ActionResult Edit(int promoId) { ... }
}


六、路由约束

路由约束可以让我们去限制怎样去匹配路由参数,例如

// eg: /users/5
[Route("users/{id:int}"]
public ActionResult GetUserById(int id) { ... }

// eg: users/ken
[Route("users/{name}"]
public ActionResult GetUserByName(string name) { ... }


这里例子,第一个路由仅当id是整数时,才被匹配;否则第二个路由将被匹配

下表列出了可支持的约束下表列出了可支持的约束

ConstraintDescriptionExample
alphaMatches uppercase or lowercase Latin alphabet characters (a-z, A-Z){x:alpha}
boolMatches a Boolean value.{x:bool}
datetimeMatches a DateTime value.{x:datetime}
decimalMatches a decimal value.{x:decimal}
doubleMatches a 64-bit floating-point value.{x:double}
floatMatches a 32-bit floating-point value.{x:float}
guidMatches a GUID value.{x:guid}
intMatches a 32-bit integer value.{x:int}
lengthMatches a string with the specified length or within a specified range of lengths.{x:length(6)}
{x:length(1,20)}
longMatches a 64-bit integer value.{x:long}
maxMatches an integer with a maximum value.{x:max(10)}
maxlengthMatches a string with a maximum length.{x:maxlength(10)}
minMatches an integer with a minimum value.{x:min(10)}
minlengthMatches a string with a minimum length.{x:minlength(10)}
rangeMatches an integer within a range of values.{x:range(10,50)}
regexMatches a regular expression.{x:regex(^\d{3}-\d{3}-\d{4}$)}
要注意一些约束,例如 min ,他的参数要用圆括号 ”()" 括起来
你也可以对一个参数用多个约束,用冒号 ":"隔开,例如

// eg: /users/5
// but not /users/10000000000 because it is larger than int.MaxValue,
// and not /users/0 because of the min(1) constraint.
[Route("users/{id:int:min(1)}")]
public ActionResult GetUserById(int id) { ... }


如果你想去指定一个参数可选的话就可以用 "?",他应该放到所有约束的最后面,代码如下:

// eg: /greetings/bye
// and /greetings because of the Optional modifier,
// but not /greetings/see-you-tomorrow because of the maxlength(3) constraint.
[Route("greetings/{message:maxlength(3)?}")]
public ActionResult Greet(string message) { ... }


七、自定义约束

我们也可以通过实现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
{
// eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin
[Route("temp/{scale:values(celsius|fahrenheit)}")]
public ActionResult Show(string scale)
{
return Content("scale is " + scale);
}
}


八、路由名字

我们也可以为路由指定一个名字,这样可以方便我们去生成它

[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }


<a href="@Url.RouteUrl("mainmenu")">Main menu</a>


哈哈,很爽吧

九、Areas

我们可以使用
RouteArea
属性,去让控制器属于一个Area

[RouteArea("Admin")]
[RoutePrefix("menu")]
[Route("{action}")]
public class MenuController : Controller
{
// eg: /admin/menu/login
public ActionResult Login() { ... }

// eg: /admin/menu/show-options
[Route("show-options")]
public ActionResult Options() { ... }

// eg: /stats
[Route("~/stats")]
public ActionResult Stats() { ... }
}


下面的链接最终生成的就是"/Admin/menu/show-options"了

Url.Action("Options", "Menu", new { Area = "Admin" })


如果我们在同时使用用Areas和属性路由,Areas的路由基于我们在AreaRegistration中设置的,你需要去确保在配置中Area注册是在MVC 属性路由注册之后,对于这两种配置,我们都应该在 默认路由之前去注册。原因我想我不用说都应该很清楚吧,哪一个先去注册,那一个路由配置就最先去匹配URI,匹配不成功再交给下一个路由配置,例如

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 }
);
}


终于翻译完了,由于这是本人是第一次翻译,英语水平也不太好,可能会有部分中式英语在里面以及一些其他不对的问题。如果有博友发现有问题我会立即更正的,谢谢!

原文链接在顶部已经贴出来了,有兴趣的也可以去看一下原文。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: