Web API-属性路由
路由(Routing)就是Web API如何将一个URI匹配到一个action的过程。Web API 2 支持一个新的路由方式-属性路由(attribute routing)。顾名思义,属性路由使用标识属性去定义路由,属性路由可以使你在Web API中更方便的定制你的URIs。例如,你可以很容易的创建描述不同层次资源的URIs。
前面将的路由风格,叫做基于约定的路由(convention-based)在Web API中也完全支持,实际上,你能够在项目中同时使用两种路由技术。
这篇文章主要演示如何在项目中启用属性路由(attribute routing)和描述各种属性路由的使用方式,主要内容:
--1、为什么需要属性路由
--2、允许属性路由
--3、添加路由属性
--4、路由前缀
--5、路由约束
--6、可选择的URI参数以及默认值
--7、路由名称
--8、路由顺序
1、为什么需要属性路由
第一个发布版的Web API使用 convention-based routing(基于约定的)。在这种风格的路由中,你定义一个或者多个路由模版,基本上是参数化的字符串。当框架接收到一个请求,框架将URI与路由模版进行匹配。
convention-based路由的一个优势是:路由模版定义在一个文件中,路由规则被应用到所以的控制器上。但是convention-based方式的路由风格,要实现支持像RESTful APIs中常见的特定的URI模式比较麻烦。例如,资源经常包含子资源:Customers have orders, movies have actors, books have authors,等等。创建能够反映这种关系的URI是必须的:/customers/1/orders
使用convention-based 路由很难去实现这种风格的URI(This type of URI is difficult to create using convention-based routing),尽管可以实现,但结果不佳,如果你有很多控制器和资源类型。
使用属性路由,你可以很容易的为这样的URI定义一个路由,只需要给一个控制器的action添加一个标识属性:
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
还有一些情况下使用属性路由将非常方便:
--API 版本(API versioning)
/api/v1/products
/api/v2/products
解释:假设要控制请求访问不同版本的api,如果是convention-based风格的路由,意味着这里的v1 ,v2被定义为参数,那么必须在action中接收这个参数,然后在action中才能判断出版本(貌似这个时候知道版本用处不大了)我们要实现的是v1 ,v2 访问的是不同的控制器。那么使用属性路由很容易实现这一点,比如一个方法只在v2中有:
[Route("/api/v2/products")]
public Ienumerable<Product> GetAll(){}
(如何实现版本控制,细节可能要在项目中去感受)
--重载 URI片段(Overloaded URI segments)
/orders/1
/orders/pending
这个例子中"1"是一个订单编号,但是"pending"对应了一个订单集合。
--复杂的参数类型
/orders/1
/orders/2013/06/16
这个例子中"1" 是一个订单编号,但是"2013/06/16"指定了一个日期。
2、允许属性路由
Global.asax文件
protected void Application_Start()
{
// Pass a delegate to the Configure method.
GlobalConfiguration.Configure(WebApiConfig.Register);
}
WebApiConfig类
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes(); // Other Web API configuration not shown.
}
config.MapHttpAttributeRoutes()方法启用属性路由。
3、添加路由标识属性
一个例子:
public class OrdersController : ApiController
{
[Route("customers/{customerId}/orders")]
[HttpGet]
public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}
字符串"customers/{customerId}/orders"是路由的URI模版,Web API尝试将请求URI与这个模版匹配,这个例子中"coustomers" 和 "orders" 是纯文本片段,{customerId}是占位符,下面的URI都会与这个模版匹配:
- http://localhost/customers/1/orders
- http://localhost/customers/bob/orders
- http://localhost/customers/1234-5678/orders
可以使用约束限制{customerId}匹配的范围,下面会讲到。
注意在路由模版中的{customerId}参数,和action方法中的customerId参数匹配,当Web API调用控制器的action,将进行参数绑定,例如如果URI是: http://example.com/customers/1/orders
Web API会将值“1”传递的action方法的customerId参数。一个URI模版可以有多个占位符参数:
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
--HTTP请求方式
默认情况下Action方法使用方法开头用请求方式名称的方式两匹配不同的HTTP请求(忽略大小写的),可以通过添加标识属性来指定某一个action方法匹配的HTTP请求方式:
[HttpDelete]
[HttpGet]
[HttpHead]
[HttpOptions]
[HttpPatch]
[HttpPost]
[HttpPut]
例如:
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }// WebDAV method
[Route("api/books")]
[AcceptVerbs("MKCOL")]
public void MakeCollection() { }
4、路由前缀
很多时候一个控制器下的action路由模版的前面部分都是相同的,为了不重复书写,可以这样:
[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")] 给控制器下的所有action方法增加了路由模版前缀。
如果有特殊情况,你可以在action方法的标识属性中使用浪符号(~)来覆盖指定的统一的路由前缀:
[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) { ... }
GetUserById 方法只匹配id参数为整型的URI
支持的约束条件:
Constraint |
Description |
Example |
alpha |
Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) |
{x:alpha} |
bool |
Matches a Boolean value. |
{x:bool} |
datetime |
Matches a DateTime value. |
{x:datetime} |
decimal |
Matches a decimal value. |
{x:decimal} |
double |
Matches a 64-bit floating-point value. |
{x:double} |
float |
Matches a 32-bit floating-point value. |
{x:float} |
guid |
Matches a GUID value. |
{x:guid} |
int |
Matches a 32-bit integer value. |
{x:int} |
length |
Matches a string with the specified length or within a specified range of lengths. |
{x:length(6)} |
long |
Matches a 64-bit integer value. |
{x:long} |
max |
Matches an integer with a maximum value. |
{x:max(10)} |
maxlength |
Matches a string with a maximum length. |
{x:maxlength(10)} |
min |
Matches an integer with a minimum value. |
{x:min(10)} |
minlength |
Matches a string with a minimum length. |
{x:minlength(10)} |
range |
Matches an integer within a range of values. |
{x:range(10,50)} |
regex |
Matches a regular expression. |
{x:regex(^\d{3}-\d{3}-\d{4}$)} |
有些约束条件可以组合使用,比如"min":必须为整型且大于或等于1
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { ... }
自定义路由约束
通过实现IHttpRouteConstraint接口来创建自定义的路由约束,例如下面的代码定义了一个“不能为0”的整数约束。
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 != ;
} string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
if (Int64.TryParse(valueString, NumberStyles.Integer,
CultureInfo.InvariantCulture, out longValue))
{
return longValue != ;
}
}
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);
}
}
现在就可以在路由中使用这个自定义的约束条件了:id必须为非0整数
[Route("{id:nonzero}")]
public HttpResponseMessage GetNonZero(int id) { ... }
还可以通过实现IInlineConstraintResolver 接口的方式来覆盖所有的内置的约束。
6、可选择的URI参数和默认值
你可以通过给路由参数添加 问号"?”的方式来标识这个参数是可选的,如果路由模版中定义了可选参数,那么必须为action方法参数指定一个默认值(可选参数)。
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid:int?}")]
public IEnumerable<Book> GetBooksByLocale(int lcid = ) { ... }
}
上面这个例子,
/api/books/locale/1033 和
/api/books/locale
将返回同样的资源
还可以在路由模版中直接指定默认值:
public class BooksController : ApiController
{
[Route("api/books/locale/{lcid:int=1033}")]
public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}
上面两个例子功能基本相同,但也有细微的差别:
--在第一个例子中“{lcid:int?}”,默认值直接在action方法的参数位置指定,所以action方法有一个确定类型的默认值。
--第二个例子中“{lcid=1033}”,因为在路由模版中指定的默认值,那么需要经过模型绑定的过程,模型绑定过程会将“1033”从字符类型转换成数字类型,如何自定义了模型绑定方式,可能还有其他的不同的地方。
两个例子的功能一般情况下都是相同的,除非自定义了模型绑定。
7、路由名称
在Web API中每一个路由项都有一个名称,路由名称在生成链接的时候非常有用,隐藏你可以在返回消息中包含一个有效的链接。
使用Name 属性指定路由名称,下面的例子展示了如何定义路由名称,以及如何使用路由名称生成链接:
public class BooksController : ApiController
{
[Route("api/books/{id}", Name="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;
}
}
8、路由顺序
当框架尝试去将一个URI匹配到一个路由时,会给路由进行排序,如果需要自定义顺序,可以在路由标识属性中使用RouteOrder 属性,较小的值排在前面,默认的排序值是0。
排序是如何确定的:
1.比较路由标识属性的RouteOrder属性值。
2.查看路由模版中的每一个URI片段,对于每一个片段,按照下面的方式排序
1-纯文本片段
2-带约束条件的路由参数
3-不带约束条件的路由参数
4-带约束条件的通配符路由参数
5不带约束条件的通配符路由参数
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 = )]
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
前面有讲过,在URI匹配路由模版时是从路由的排列顺序开始匹配,一旦匹配成功则会忽略后面的路由模版了。
Web API-属性路由的更多相关文章
- Web Api 的 路由机制
ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动设备)的 HTTP 服务. ASP.NET Web API 是一种用于在 .NET Framework 上构 ...
- ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?
ASP.NET MVC 的路由实际上是建立在 ASP.NET 的路由系统之上的. MVC 路由注册通常是这样的: RouteTable 是一个全局路由表, 它的 Routes 静态属性是一个 Ro ...
- Web API之路由浅谈
Web API的路由,是指明接口地址的方向,是照亮获取数据路上的灯塔,其重要性不言而喻. 本篇文章以vs2015为例,一步步说明路由的创建及使用,其中包括默认路由.自定义路由和特性路由. 一.默认路由 ...
- 2.3属性在 ASP.NET Web API 2 路由
路由是 Web API 如何匹配 URI 的行动.Web API 2 支持一种新型的路由,称为属性路由.顾名思义,属性路由使用属性来定义路由.属性路由给你更多的控制 Uri 在您的 web API.例 ...
- 2.4使用属性在 ASP.NET Web API 2 路由创建一个 REST API
Web API 2 支持一种新型的路由,称为属性路由.属性路由的一般概述,请参阅属性路由 Web API 2 中.在本教程中,您将使用属性路由创建一个 REST API 集合的书.API 将支持以下操 ...
- ASP.NET Web API编程——路由
路由过程大致分为三个阶段: 1)请求URI匹配已存在路由模板 2)选择控制器 3)选择操作 1匹配已存在的路由模板 路由模板 在WebApiConfig.Register方法中定义路由,例如模板默认生 ...
- [翻译]ASP.NET Web API的路由
原文:Routing in ASP.NET Web API 在我们新建一个Web API项目时,会在App_Start文件夹下的WebApiConfig.cs中定义一个默认路由: config.Rou ...
- ASP.NET WEB API 特性路由
一.什么是特性路由? 特性路由是指将RouteAttribute或自定义继承自RouteAttribute的特性类标记在控制器或ACTION上,同时指定路由Url字符串,从而实现路由映射,相比之前的通 ...
- ASP.NET Web API 路由对象介绍
ASP.NET Web API 路由对象介绍 前言 在ASP.NET.ASP.NET MVC和ASP.NET Web API这些框架中都会发现有路由的身影,它们的原理都差不多,只不过在不同的环境下作了 ...
- ASP.NET Web API路由系统:路由系统的几个核心类型
虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集System.Web.Http.dll中)已经移除 ...
随机推荐
- jQuery Mobile发展新闻阅读器,适应iphone和android打电话
程序猿是很不赖,你知道. 我们经常新浪,腾讯.雅虎等各大网站看到上述新闻.他们还推出了自己的移动新闻阅读器.今天,我自己用的jQuery Mobile 为了实现这一功能,.图像大小上传限制的大小250 ...
- Spring : 征服数据库 (两)
本节介绍Spring和ORM集成框架.尽管Hibernate在开源ORM 社区很受欢迎.但是,本文将MyBatis案例解说.也MyBatis和Hibernate好坏是没有意义的,主要看实际需求,有兴趣 ...
- Docker简明教程(转)
Docker自从诞生以来就一直备受追捧,学习Docker是一件很炫酷.很有意思的事情.我希望通过这篇文章能够让大家快速地入门Docker,并有一些学习成果来激发自己的学习兴趣.我也只是一个在Docke ...
- [SignalR]在非Hub继承类中使用脚本方法
原文:[SignalR]在非Hub继承类中使用脚本方法 新建一个普通类OutHub,里面包含一个脚本方法OutHubTest. 因为大家知道,若能让脚本调用到的话,必须继承Hub,那怎么实现了?通过G ...
- Docker container communication with ovs
2台宿主机,192.168.11153,192.168.1.154 安装OVS rpm -ivh openvswitch-2.4.0-1.x86_64.rpm #预先下载的 配置OVS 1. 按照上一 ...
- Ajax 实现无刷新页面
注意:如本文所用,在前面的文章库的数目可以在源代码中找到,我将指示在文本,其中链路,为了缩短制品的长度,阅读由此带来的不便.乞求被原谅. 评论文章 Ajax 实现无刷新页面.其原理.代码库.代码. 这 ...
- Learn Python More
0, 看了一个python项目开源源码, 才知道现在这点python知识实在是弱爆了.. 尼玛就像学了2500个常用汉字, 然后要去理解"楚辞".. 代码如下, 解释一点一点从网上 ...
- UVA 11987 - Almost Union-Find(并查集)
UVA 11987 - Almost Union-Find 题目链接 题意:给定一些集合,操作1是合并集合,操作2是把集合中一个元素移动到还有一个集合,操作3输出集合的个数和总和 思路:并查集,关键在 ...
- 十天学Linux内核之第二天---进程
原文:十天学Linux内核之第二天---进程 都说这个主题不错,连我自己都觉得有点过大了,不过我想我还是得坚持下去,努力在有限的时间里学习到Linux内核的奥秘,也希望大家多指点,让我更有进步.今天讲 ...
- Python challenge 3 - urllib & re
第三个主题地址:http://www.pythonchallenge.com/pc/def/ocr.html Hint1:recognize the characters. maybe they ar ...