一、属性路由的概念

  路由让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中的路由(二)——属性路由的更多相关文章

  1. 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由

    原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...

  2. Web API中的路由(一)——约定路由

    一.Web API中的路由概念 路由的作用用一句话说明:通过request的uri找到处理该请求的Controller,Action,以及给Action的参数赋值. 一些路由的基本概念: route: ...

  3. Web APi入门之Self-Host寄宿及路由原理(二)

    前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...

  4. ASP.NET WEB API 中的路由调试与执行过程跟踪

    路由调试 RouteDebugger 是调试 ASP.NET MVC 路由的一个好的工具,在ASP.NET WEB API中相应的有 WebApiRouteDebugger ,Nuget安装 Inst ...

  5. ASP.NET Web API中的Routing(路由)

    [译]Routing in ASP.NET Web API 单击此处查看原文 本文阐述了ASP.NET Web API是如何将HTTP requests路由到controllers的. 如果你对ASP ...

  6. 在 ASP.NET Web API 中,使用 命名空间(namespace) 来作为路由的参数

    这个问题来源于我想在 Web API 中使用相同的控制器名称(Controller)在不同的命名空间下,但是 Web API 的默认 路由(Route) 机制是会忽略命名空间的不同的,如果这样做,会看 ...

  7. Web APi入门之Self-Host寄宿及路由原理

    前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...

  8. Web APi入门之Self-Host寄宿及路由原理 【转载】

    前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...

  9. ASP.NET Web API 控制器创建过程(二)

    ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来发布的,由于身体跟不上节奏感冒发烧有心无力,这种天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病 ...

随机推荐

  1. D - Mayor's posters POJ - 2528 离散化+线段树 区间修改单点查询

    题意 贴海报 最后可以看到多少海报 思路 :离散化大区间  其中[1,4] [5,6]不能离散化成[1,2] [2,3]因为这样破坏了他们的非相邻关系 每次离散化区间 [x,y]时  把y+1点也加入 ...

  2. HDU1800 字典树写法

    题意:高级魔法师可以教低级魔法师 魔法扫把技能,同时教会了的低级魔法师又可以教比他更低级是,是传递的关系 同时如果教会了的话,他们可以同时坐一个扫把 问最少需要多少个扫把 思路:就是判断相同的数字最多 ...

  3. AHOI2013 差异 【后缀数组】

    题目分析: 求出height以后很明显跨越最小height的一定贡献是最小height,所以对于区间找出最小height再将区间对半分. 代码: #include<bits/stdc++.h&g ...

  4. Codeforces300 F. A Heap of Heaps

    Codeforces题号:#300F 出处: Codeforces 主要算法:树状数组/线段树 难度:4.6 思路分析: 在没看到数据范围之前真是喜出望外,直到发现O(n^2)会被卡…… 其实也不是特 ...

  5. Min_25

    可以用来筛出一个积性函数的前缀和.这个积性函数要满足当\(x\)是质数时,\(f(x)\)可以快速求出,\(f(x^k)\)也可以快速算出. 首先我们要处理出一个\(g(x)=\sum_{x\in p ...

  6. Educational Codeforces Round 33 (Rated for Div. 2) F. Subtree Minimum Query(主席树合并)

    题意 给定一棵 \(n\) 个点的带点权树,以 \(1\) 为根, \(m\) 次询问,每次询问给出两个值 \(p, k\) ,求以下值: \(p\) 的子树中距离 \(p \le k\) 的所有点权 ...

  7. Leetcode 27.移除元素 By Python

    给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...

  8. 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 ...

  9. 每天一个linux命令(1):wc命令

    Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数.字数.行数,并将统计结果显示输出. 1.命令格式: wc [选项]文件... 2.命令功能: 统计指定文件中的字节数. ...

  10. luogu P4299 首都

    题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...