zoukankan      html  css  js  c++  java
  • 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(6)} {x:length(1,20)}
        max/min                 最大值/最小值            {x:max(10)}
        maxlength/minlength     最大/最小长度            {x:maxlength(10)}
        range                   整数在一定范围内         {x:range(10,50)}
        regex                   匹配正则表达式           {x:regex(^d{4}-d{2}-d{2}$)}

    约束可以多个一起,用:隔开如  users/{id:int:max(1000):min(1)} 

    2.3.3  可选uri参数和设置参数的默认值

    直接看栗子:

    public class BooksController : ApiController
    {
      [Route("api/orders/coustomer/{coustomerId:int?}")]
      public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
    }

    当我们访问http://xxx/api/orders/coustomer/和访问http://xxx/api/orders/coustomer/1033获取的结果是一样的,在设置参数可选时必须设置默认值

    三、路由优先级

      在掌握路由的优先级前,我们先要了解一个属性: RouteOrder属性 。在属性路由中,我们可以设置RouteOrder属性,如 [Route("orders/{id}",RouteOrder=1)] ,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 = 1)] //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中的属性路由

  • 相关阅读:
    函数-列表生成式
    函数-闭包
    函数-参数
    函数-装饰器
    函数-函数递归
    函数-高阶函数
    函数-命名空间
    函数-匿名函数
    模块-shutil
    在 Android 5.1.1 执行 remount system failed 解决方法
  • 原文地址:https://www.cnblogs.com/wyy1234/p/9475713.html
Copyright © 2011-2022 走看看