Web API 2中的属性路由
Web API 2中的属性路由
前言
阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html
路由就是Web API如何把URI匹配到一个Action的描述。Web API支持一种新的路由类型,被叫做属性路由。顾名思义,属性路由是用属性来创建路由。在你的Web API中属性路由可以让你更好的控制URI。你能容易的创建描述资源阶层的URIs。
较早的基于公约的路由风格是全面被支持的。事实上,你能够在同一个项目中联合使用这两种技术。
本文主要展示如何启用属性路由,并且描述了属性路由的各种选项,内容如下:
1、为什么使用属性路由?
2、启用属性路由
3、添加路由属性
4、路由前缀
5、路由约束
6、可选的URI参数和默认值
7、路由名称
8、路由顺序
1、为什么使用属性路由
第一个Web API版本使用的是基于公约的路由。在该类型的路由中, 你可以定义一个或者多个被参数化字符串的模版。当这个框架接收到一个请求时,它匹配一个URI到路由模版。有关基于公约的路由的详细介绍可以参考之前的文章:http://www.cnblogs.com/aehyok/p/3442051.html
基于公约的路由的一个优势就是,这个模版被定义在一个单独的地方。这个路由规则一致的被应用于所有的控制器。不幸的是,基于公约的路由是很难支持确切的URI模式,而这个确切的URI模式在Restful APIs中是很普遍的。例如,资源经常包含子资源:客户下了订单,电影有演员,书有作者等等,它是很自然的创建这些URI来反应这些关系:
/customers/1/orders
这种类型的URI在基于公约的路由下是比较难实现的。尽管它能做到,但是如果你有许多控制器或者很多资源类型这种结果不能很好的被扩展。
对于属性路由,它是很容易的为这个URI定义一个路由。你可以简单的添加一个属性到控制器的动作上:
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
这有一些属性路由使它更容易的其他URI模式。
API版本控制
在下面的例子中,"api/v1/products"相对于"api/v2/products"将被路由到不同的控制器。
/api/v1/products
/api/v2/products
重载URI片段
在下面的例子中,"1"是一个阶数,而"pending"被映射到集合。
/orders/1
/orders/pending
多个参数类型
在下面的例子中,"1"是一个阶数,而“2013/06/16”被指定为一个日期。
/orders/1
/orders/2013/06/16
2、启用属性路由
要启用属性路由,在配置期间需要调用MapHttpAttributeRoutes。这个扩展方法被定义在System.Web.Http.HttpConfigurationExtensions类中。

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}

你也可以将属性路由与基于公约的路由一起使用。为了定义基于公约的路由,需要调用MapHttpRoute 的方法。

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

3、添加路由属性
下面是一个使用属性定义的路由示例:
public class OrdersController : ApiController
{
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}
这个[Route]属性定义了一个HTTP Get方法。这个字符串“customers/{customerId}/orders”是路由的URI模版。在路由模版中的“{customerId}”参数匹配了在方法中的customerId参数的名称。 例如,这个路由将匹配如下的URI:
http://example.com/customers/1/orders
这个URI模版可以有多个参数:
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
任何没有路由属性的控制器方法将使用基于公约的路由。这种方式,你可以结合两种方式在同一个项目中。
4、路由前缀
通常情况下,在同一个控制器中的所有路由以相同的前缀开头。例如:

public class BooksController : ApiController
{
[Route("api/books")]
public IEnumerable<Book> GetBooks() { ... } [Route("api/books/{id:int}")]
public Book GetBook(int id) { ... } [Route("api/books")]
public HttpResponseMessage CreateBook(Book book) { ... }
}

对于整个控制器你能通过一个[RoutePrefix]属性来设置一个公共的前缀:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/books
[Route("")]
public IEnumerable<Book> Get() { ... }
// GET api/books/5
[Route("{id:int}")]
public Book Get(int id) { ... }
// POST api/books
[Route("")]
public HttpResponseMessage Post(Book book) { ... }
}

在方法属性上可以用一个波浪符号重写路由前缀:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET /api/authors/1/books
[Route("~/api/authors/{authorId:int}/books")]
public IEnumerable<Book> GetByAuthor(int authorId) { ... }
// ...
}

路由前缀可以包含参数:

[RoutePrefix("customers/{customerId}")]
public class OrdersController : ApiController
{
// GET customers/1/orders
[Route("orders")]
public IEnumerable<Order> Get(int customerId) { ... }
}

5、路由约束
路由约束可以让你在路由模版中限制参数被匹配。常规的语法是"{parameter:constraint}",例如:
[Route("users/{id:int}"]
public User GetUserById(int id) { ... }
[Route("users/{name}"]
public User GetUserByName(string name) { ... }
如果URI的“id”片段是一个integer类型的,那么第一个路由将会被选择,否则第二个路由将会被选择。
下面是被支持的约束列表:


注意到一些限制,例如"min",带参数在括号里。您可以应用多个约束的参数,用冒号分隔。
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { ... }
自定义路由约束
你能够创建自定义的路由约束通过实现这个IHttpRouteConstraint 接口。例如,以下约束将一个参数限制为一个非零的整数值。

public class NonZeroConstraint : IHttpRouteConstraint
{
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
long longValue;
if (value is long)
{
longValue = (long)value;
return longValue != 0;
} string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
if (Int64.TryParse(valueString, NumberStyles.Integer,
CultureInfo.InvariantCulture, out longValue))
{
return longValue != 0;
}
}
return false;
}
}

下面的代码演示如何注册约束:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint)); config.MapHttpAttributeRoutes(constraintResolver);
}
}

现在你可以在你的路由中应用这个约束:
[Route("{id:nonzero}")]
public HttpResponseMessage GetNonZero(int id) { ... }
你也能替换整个DefaultInlineConstraintResolver 类通过实现IInlineConstraintResolver 接口。这样做将替换所有的内置约束。除非在IInlineConstraintResolver 的实现特地的添加它们。
6、可选的URI参数和默认值
你可以通过添加一个问号标记路由参数使成为一个可选的URI参数。如果一个路由参数是可选的,你必须为这个方法参数定义一个默认值。
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid?}")]
public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}
在这个例子中,"/api/books/locale/1033"和"/api/books/locale"将返回同样的资源。
或者,你能在路由模版中定义一个默认值,如下:
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid=1033}")]
public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}
这个示例几乎和上面的例子一样。但是当默认值被应用的时候,行为上有一个轻微的不同。
1、在第一个例子中("{lcid?}"),1033默认值被直接指定在方法参数上。因此这个参数将有一个精确的值。
2、在第二个例子中("{lcid?}"),1033默认值通过模型绑定进程。这个默认的模型绑定将转换“1033”为1033数值。但是,你可以插入自定义模型绑定,其中可能做不同的事情。
在大多数情况下,除非在你的管道中有自定义的模型绑定,这两种表现是等价的。
7、路由名称
在Web API中,每个路由都有一个名称。路由名称被用于生成链接,你能在HTTP响应中包含一个链接。
指定这个路由名称,在这个属性上设置RouteName属性。下面的例子展示怎样设置路由名称,当生成一个链接也能用这个路由名称。

public class BooksController : ApiController
{
[Route("api/books/{id}", RouteName="GetBookById")]
public BookDto GetBook(int id)
{
// Implementation not shown...
} [Route("api/books")]
public HttpResponseMessage Post(Book book)
{
// Validate and add book to database (not shown) var response = Request.CreateResponse(HttpStatusCode.Created); // Generate a link to the new book and set the Location header in the response.
string uri = Url.Link("GetBookById", new { id = book.BookId });
response.Headers.Location = new Uri(uri);
return response;
}
}

如果你不设置RouteName 属性,Web API产生这个名字。这个默认的路由名称是"ControllerName.ActionName"。在前面的例子中,对于这个GetBook 方法这个默认的路由名称将是“Books.GetBook”。对于同一个动作名称如果控制器有多重的属性路由,一个后缀将被添加。例如,“Books.GetBook1" 和 "Books.GetBook2"。
8、路由顺序
当一个框架试图讲一个URI匹配到路由的时候,它会在特定的顺序下评估这些路由。为了指定这个顺序,在路由属性上设置RouteOrder 属性。较低的值将首先被评估。这默认的顺序值是0。
这里是如何确定的总排序:
1.比较路由属性中的RouteName 属性。
2.在路由模版中查看每个URI片段。对于每个片段,顺序如下:
- 文本片段。
- 带有约束的路由参数。
- 不带有约束的路由参数。
- 带有约束的通配符路由参数。
- 不带有约束的通配符路由参数。
3.In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.(这句话还没搞太明白)
下面是一个示例。假设你定义以下控制器:

[RoutePrefix("orders")]
public class OrdersController : ApiController
{
[Route("{id:int}")] // constrained parameter
public HttpResponseMessage Get(int id) { ... }
[Route("details")] // literal
public HttpResponseMessage GetDetails() { ... }
[Route("pending", RouteOrder = 1)]
public HttpResponseMessage GetPending() { ... }
[Route("{customerName}")] // unconstrained parameter
public HttpResponseMessage GetByCustomer(string customerName) { ... }
[Route("{*date:datetime}")] // wildcard
public HttpResponseMessage Get(DateTime date) { ... }
}

这些路由进行排序,如下所示:
1.orders/details
2.orders/{id}
3.orders/{customerName}
4.orders/{*date}
5.orders/pending
注意"details"是一个文本片段出现在"{id}"之前,但是"pending"将出现在最后因为这个RouteOrder 属性是1。(这个例子假定没有客户命名为"details"或者"pending")。总之,试图去避免模糊不清的路由。在这个例子中,GetByCustomer 的一个较好的路由模版是"customers/{customerName}")。
总结
属性路由,很不错,随心所欲,想怎么定义很方便,真是一大亮点吧。
本文参考链接http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
本文同时也已经更新至系列导航中http://www.cnblogs.com/aehyok/p/3446289.html
Web API 2中的属性路由的更多相关文章
- ASP.NET Web API 2 中的属性路由使用(转载)
转载地址:ASP.NET Web API 2 中的属性路由使用
- ASP.NET Web API 2中的属性路由(Attribute Routing)
如何启用属性路由并描述属性路由的各种选项? Why Attribute Routing? Web API的第一个版本使用基于约定的路由.在这种类型的路由中,您可以定义一个或多个路由模板,这些模板基本上 ...
- Asp.Net Web API 2第八课——Web API 2中的属性路由
前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 路由就是Web API如何 ...
- ASP.NET Web API 2 中的特性路由
ASP.NET MVC 5.1 开始已经支持基于特性的路由(http://attributerouting.net),ASP.NET WEB API 2 同时也支持了这一特性. 启用特性路 由只需要在 ...
- 【ASP.NET Web API教程】4.2 路由与动作选择
原文:[ASP.NET Web API教程]4.2 路由与动作选择 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内容. 4.2 Routing ...
- 从ASP.Net Core Web Api模板中移除MVC Razor依赖项
前言 :本篇文章,我将会介绍如何在不包括MVC / Razor功能和包的情况下,添加最少的依赖项到ASP.NET Core Web API项目中. 一.MVC VS WebApi (1)在ASP. ...
- [译] 在Web API 2 中实现带JSON的Patch请求
原文链接:The Patch Verb in Web API 2 with JSON 我想在.NET4.6 Web API 2 项目中使用Patch更新一个大对象中的某个字断,这才意识到我以前都没有用 ...
- Web API (四) 特性路由(Attribute Route)
特性路由 是Web API 2 中提出的一种新的类型的路由,正如其名称那样,它是通过特性(Attribute) 来定义路由的,相比之前的基于模式(Convertion Based)的路由,特性路由 能 ...
- 【Web API系列教程】1.2 — Web API 2中的Action Results
前言 本节的主题是ASP.NET Web API怎样将控制器动作的返回值转换成HTTP的响应消息. Web API控制器动作能够返回下列的不论什么值: 1. void 2. HttpResponseM ...
随机推荐
- iOS使用UIScrollView实现左右滑动UITableView和UICollectionView
在UIScrollView嵌套UITableView这篇文章是非常,但该项目的需求,需要嵌套UICollectionView,和UICollectionView和UITableView有非常多的不同, ...
- linux_ Redhat Linux配置JDK和Tomcat需要注意的地方
转:http://blog.csdn.net/hongdi/article/details/10525797 1.操作系统和安装包操作系统:Redhat Linux 6.4服务器版,桌面安装JDK:j ...
- 在VirtualBox安装OS X 10.10
下面将指导介绍了如何引入自由和强大VirtualBox安装在虚拟机上OS X Yosemite 10.10 法律免责声明:本指南旨在说明如何在定期购买的苹果电脑上创建一个虚拟机执行真正的Mac OS ...
- ENode 2.0
ENode 2.0 - 介绍一下关于ENode中对Command的调度设计 摘要: CQRS架构,C端的职责是处理从上层发送过来的command.对于单台机器来说,我们如何尽快的处理command呢? ...
- Socket 学习(三).5 UDP 的弱点
前面 讲到了,udp 传输文本的例子,发现 udp 确实 比tcp 高效一些,现在我用来传输文件,问题果然来了,结果发现 他不能一次 传输大于 64K的东西! 那么 我自然想到了 切包,多次发送,再合 ...
- iOS开发的一些奇巧淫技2
能不能只用一个pan手势来代替UISwipegesture的各个方向? - (void)pan:(UIPanGestureRecognizer *)sender { typedef NS_ENUM(N ...
- cocos2d-x 3.0 rapidJson 解析操作应该注意的细节
Size visibleSize = Director::getInstance()->getVisibleSize(); Point origin = Director::getInstanc ...
- dom03
鼠标事件: 键盘事件: //通过class获取元素,封装一个通过class获取元素的方法 //IE10以下不支持document.getElementByClass() function getByC ...
- Appium0.18.x迁移到Appium1.x须知事项
英文原版:https://github.com/appium/appium/blob/master/docs/en/advanced-concepts/migrating-to-1-0.md Migr ...
- Spring AspectJ的Execution表达式-备忘笔记
Aspectj切入点语法定义 在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点" 例如定义切入点表达式 execu ...