Web API中的路由(二)——属性路由
一、属性路由的概念
路由让webapi将一个uri匹配到对应的action,Web API 2支持一种新类型的路由:属性路由。顾名思义,属性路由使用属性来定义路由。通过属性路由,我们可以更好地控制Web API中的URI。例如,我们可以轻松创建描述资源层次结构的URI。一个栗子:api/customers/1/orders,我们很容易猜到这个路由的作用是找Id为1的客户的所有订单。但是基于约定的路由很难创建这样的uri。
早期的基于约定的路由仍然完全受支持。实际上,我们可以在同一个项目中组合这两种技术,值得注意的是约定路由不能去匹配有路由属性的Action。
二、属性路由的使用
2.1 启用属性路由
2.1.1 注册属性路由
在Web API 2中,我们新建一个项目,在App_start文件下 WebApiConfig.cs 文件中规定了默认的路由,采用的是两种路由方法结合使用。 config.MapHttpAttributeRoutes() 的作用是调用属性路由,而 config.Routes.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 }
);
}
}
2.1.2 添加路由属性
还是以上边获取特定Id的客户的订单为例,使用属性路由来实现十分简单,直接的方法上添加一个路由属性即可,代码如下:
public class OrdersController : ApiController
{
[Route("customers/{customerId}/orders")]
[HttpGet]
public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}
代码中字符串 customers/{customerId}/orders 是路由模板,它可以匹配的uri有
http://localhost/customers/1/orders
http://localhost/customers/bob/orders
http://localhost/customers/bob/orders
注意参数绑定:
在上边的栗子中{customerId}会自动绑定到方法的参数customerId上去,路由模板中也可以有多个参数,一个栗子:
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
这里{customerId}和{orderId}的值会绑定到方法的参数customerId和orderId上
2.2 HTTP方法
这里和约定路由一样,属性路由也会通过请求的方式(Get/Post)找到以Get/Post等开头的Action进行匹配,我们可以通过属性修饰方法(如[HttpPost])来覆盖这个约定
一个栗子:
[Route("api/orders/{orderId}")]
public HttpResponseMessage GetOrder(int orderId) { ... }
这时我们通过get的方式访问时api/orders/1,会匹配到上边action。如果我们想使用post的方式获取订单信息,但是不想改变方法名(GetOrder改成PostOrder),怎么办呢?只需要添加一个特性修饰方法[HttpPost]即可,如果我们想让这个方法同时匹配get/post那么我们设置特性修饰方法[AcceptVerbs("GET","POST")],这里和约定路由形式是一样的,就不再多说了。
2.3 路由前缀、约束、可选URI参数、默认值
2.3.1 路由前缀
通常情况下,控制器中的路由都以相同的前缀开头,如
public class BooksController : ApiController
{
[Route("api/books")]
public IEnumerable<Book> GetBooks() { ... } [Route("api/books/{id:int}")]
public Book GetBook(int id) { ... } [Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
}
我们可以使用 [RoutePrefix] 属性为整个控制器设置公共前缀
[RoutePrefix("api/customers/{customerId}/orders")]
public class OrdersController : ApiController
{
// GET api/customers/1
[Route("")]
public IEnumerable<Order> Get(int customerId) { ... } // GET api/customers/1/orders/1
[Route("{id:int}")]
public Book Get(int customerId,int id) { ... } // POST api/customers/1/orders/
[Route("")]
public HttpResponseMessage Post(int customerId,Order order) { ... }
// 注意我们可以使用~覆盖前缀
[Route("~/api/orders/put")]
public HttptResponseMessage Put(Order order)
}
2.3.2 路由约束
我们经常遇到这种情况:GetUserInfoById 和GetUserInfoByName,两个action都是只有一个参数,如果我们不加约束的话可能出现一个不好的结果:如下边栗子把id:int的约束去掉,我们用get方式访问http://users/zhangsan,默认找到第一个路由,然后把zhangsan传给id参数,这是就会抛出异常,如果添加了约束,只有id为int类型且不小于1时才会匹配GetUserById。
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { ... } [Route("users/{name}")]
public User GetUserByName(string name) { ... }
使用常用的约束除了类型约束如int ,datetime等,还有以下几种:
alpha 大小写字母 {x:alpha}
length 字符串的长度固定 {x:length()} {x:length(,)}
max/min 最大值/最小值 {x:max()}
maxlength/minlength 最大/最小长度 {x:maxlength()}
range 整数在一定范围内 {x:range(,)}
regex 匹配正则表达式 {x:regex(^\d{}-\d{}-\d{}$)}
约束可以多个一起,用:隔开如 users/{id:int:max():min()}
2.3.3 可选uri参数和设置参数的默认值
直接看栗子:
public class BooksController : ApiController
{
[Route("api/orders/coustomer/{coustomerId:int?}")]
public IEnumerable<Book> GetBooksByLocale(int lcid = ) { ... }
}
当我们访问http://xxx/api/orders/coustomer/和访问http://xxx/api/orders/coustomer/1033获取的结果是一样的,在设置参数可选时必须设置默认值。
三、路由优先级
在掌握路由的优先级前,我们先要了解一个属性: RouteOrder属性 。在属性路由中,我们可以设置RouteOrder属性,如 [Route("orders/{id}",RouteOrder=)] ,RouteOrder的值越大,路由的优先级越低,默认的RouteOrder值为0。
当一个uri可以匹配Controller中的多个Action时,我们怎么确定把这个请求交给哪个Action?
1.首先比较RouteOrder值,选出RouteOrder值小的
2.比较路由模板中的占位符
路由模板的优先级:字符串>参数有约束的>参数无约束的>参数名中含*的
补充一个知识点:
一个栗子:参数含*表示匹配uri中剩余的所有内容。如查找某一天时间的订单的路由模板: [Route("orders/date/{*date:datetime:regex(\\d{4}/\\d{2}/\\d{2})}")] ,可以匹配 http://xxx/orders/date/2017/03/04,*date匹配的是'2017/03/04',如果不加上*那么就会报错。
[RoutePrefix("orders")]
public class OrdersController : ApiController
{
[Route("{id:int}")] // 参数有约束
public HttpResponseMessage Get(int id) { ... } [Route("details")] // 字符串
public HttpResponseMessage GetDetails() { ... } [Route("pending", RouteOrder = )] //RouteOrder为1,其他的都是0
public HttpResponseMessage GetPending() { ... } [Route("{customerName}")] // 参数无约束
public HttpResponseMessage GetByCustomer(string customerName) { ... } [Route("{*date:datetime}")] // 参数名字含有*
public HttpResponseMessage Get(DateTime date) { ... }
}
当我们以get方式访问http://xxx/orders时,这些action都匹配,路由的优先级如下
orders/detail //最终匹配的,Action是GetDetails
orders/{id:int}
orders/{customerName}
orders/{*data:datetime}
order/pending
这里总结了webapi中属性路由的特点和用法,想了解更多的话,可以查看官网:webApi中的属性路由。
Web API中的路由(二)——属性路由的更多相关文章
- 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由
原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...
- Web API中的路由(一)——约定路由
一.Web API中的路由概念 路由的作用用一句话说明:通过request的uri找到处理该请求的Controller,Action,以及给Action的参数赋值. 一些路由的基本概念: route: ...
- Web APi入门之Self-Host寄宿及路由原理(二)
前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...
- ASP.NET WEB API 中的路由调试与执行过程跟踪
路由调试 RouteDebugger 是调试 ASP.NET MVC 路由的一个好的工具,在ASP.NET WEB API中相应的有 WebApiRouteDebugger ,Nuget安装 Inst ...
- ASP.NET Web API中的Routing(路由)
[译]Routing in ASP.NET Web API 单击此处查看原文 本文阐述了ASP.NET Web API是如何将HTTP requests路由到controllers的. 如果你对ASP ...
- 在 ASP.NET Web API 中,使用 命名空间(namespace) 来作为路由的参数
这个问题来源于我想在 Web API 中使用相同的控制器名称(Controller)在不同的命名空间下,但是 Web API 的默认 路由(Route) 机制是会忽略命名空间的不同的,如果这样做,会看 ...
- Web APi入门之Self-Host寄宿及路由原理
前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...
- Web APi入门之Self-Host寄宿及路由原理 【转载】
前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...
- ASP.NET Web API 控制器创建过程(二)
ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来发布的,由于身体跟不上节奏感冒发烧有心无力,这种天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病 ...
随机推荐
- D - Mayor's posters POJ - 2528 离散化+线段树 区间修改单点查询
题意 贴海报 最后可以看到多少海报 思路 :离散化大区间 其中[1,4] [5,6]不能离散化成[1,2] [2,3]因为这样破坏了他们的非相邻关系 每次离散化区间 [x,y]时 把y+1点也加入 ...
- HDU1800 字典树写法
题意:高级魔法师可以教低级魔法师 魔法扫把技能,同时教会了的低级魔法师又可以教比他更低级是,是传递的关系 同时如果教会了的话,他们可以同时坐一个扫把 问最少需要多少个扫把 思路:就是判断相同的数字最多 ...
- AHOI2013 差异 【后缀数组】
题目分析: 求出height以后很明显跨越最小height的一定贡献是最小height,所以对于区间找出最小height再将区间对半分. 代码: #include<bits/stdc++.h&g ...
- Codeforces300 F. A Heap of Heaps
Codeforces题号:#300F 出处: Codeforces 主要算法:树状数组/线段树 难度:4.6 思路分析: 在没看到数据范围之前真是喜出望外,直到发现O(n^2)会被卡…… 其实也不是特 ...
- Min_25
可以用来筛出一个积性函数的前缀和.这个积性函数要满足当\(x\)是质数时,\(f(x)\)可以快速求出,\(f(x^k)\)也可以快速算出. 首先我们要处理出一个\(g(x)=\sum_{x\in p ...
- Educational Codeforces Round 33 (Rated for Div. 2) F. Subtree Minimum Query(主席树合并)
题意 给定一棵 \(n\) 个点的带点权树,以 \(1\) 为根, \(m\) 次询问,每次询问给出两个值 \(p, k\) ,求以下值: \(p\) 的子树中距离 \(p \le k\) 的所有点权 ...
- Leetcode 27.移除元素 By Python
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...
- Hdoj 1789 Doing Homework again 题解
Problem Description Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of h ...
- 每天一个linux命令(1):wc命令
Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数.字数.行数,并将统计结果显示输出. 1.命令格式: wc [选项]文件... 2.命令功能: 统计指定文件中的字节数. ...
- luogu P4299 首都
题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...