Web Api实践系列(三)route特性使用

一、webapi 路由机制

ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程。在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一文中,体验了通过模版、惯例、HTTP方法来设置路由,这种做法的好处是把路由模版统一放在了App_Start文件夹下的WebApiConfig类中,方便管理,但缺点是不够灵活。 

REST把一切都看成资源,有时候,一个资源连带子资源,比如Customer和Orders密切关联,我们可能希望输入这样的请求:customers/1/orders,但仅仅凭借惯例,很难实现这种路由。而实际上,ASP.NET Web API为我们准备了Route特性,该特性可以直接打到Action上,使用非常灵活、直观。

二、使用Route特性

在webapi 2中不用修改任何配置,直接可以使用Route 特性,而在webapi1中需要修改WebApiConfig(具体修改配置本章不讲),下面结合实例演示一下使用Route特性

首先创建一个Customer类

 public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

再创建一个Order类

public class Order
    {
        public int Id { get; set; }
        public decimal Total { get; set; }
        public int CustomerId { get; set; }
        public Customer Customer { get; set; }
    }

再创建一个Order集合类

 public class DatabaseTest
    {
        public static IEnumerable<Order> GetOrdersByCustomerId(int customerId)
        {
            return GetOrders().Where(o => o.CustomerId == customerId);
        }


        private static IEnumerable<Order> GetOrders()

        {
            Customer cus1 = new Customer() { Id = 1, Name = "张三" };
            Customer cus2 = new Customer() { Id = 2, Name = "李四" };
            List<Order> orders = new List<Order>()
            {
                new Order(){Id = 1, Total = 80M, CustomerId = 1, Customer = cus1},
                new Order(){Id = 2, Total = 100M, CustomerId = 1, Customer = cus1},
                new Order(){Id = 3, Total = 120M, CustomerId = 2, Customer = cus2}
            };
            return orders;
        }
    }

接下来创建一个空的api控制器OrderController,并写入一下代码

namespace WebApiRout.Controllers
{
    //[RoutePrefix("api")]
    public class OrdersController : ApiController
    {
        [Route("customers/{customerId}/orders")]
        [HttpGet]
        public IEnumerable<Order> FindOrdersByCustomer(int customerId)
        {
            return DatabaseTest.GetOrdersByCustomerId(customerId);
        }
    }
}

重点讲解一下OrderController中的route特性

1.[Route("customers/{custimerId/orders}")]可以接受请求为 http://localhost/customers/1/orders

 

2.如果启用[RoutePrefix("api")] 那该方法可接受的请求变为  http://localhost/api/customers/1/orders

讲到这里就一目了然了,这两个特性的作用很明显了,各司其职!

三、路由约束

先了解一下webapi内置的约束有哪些

{x:alpha} 约束大小写英文字母

{x:bool}

{x:datetime}

{x:decimal}

{x:double}

{x:float}

{x:guid}

{x:int}

{x:length(6)}

{x:length(1,20)} 约束长度范围

{x:long}

{x:maxlength(10)}

{x:min(10)}

{x:range(10,50)}

{x:regex(正则表达式)}

路由约束举例

//可以通过"{参数变量名称:约束}"来约束路由中的参数变量。
[Route("users/{id:int}"]

public User GetUserById(int id) { ... }

//可以为一个参数变量同时设置多个约束
[Route("users/{id:int:min(1)}")]

public User GetUserByName(int id) { ... }

//在约束后面加?,表示可选,在方法参数中给id设置默认值
[Route("api/{id:int?}")]

public IEnumerable<T> Get(int id = 8){}

同样webapi提供了自定义路由约束,下面举例说明

namespace WebApiRout.Models
{
    /// <summary>
    /// 实现一个不能为0的路由约束
    /// </summary>
    public class MyHttpRouteConstraint : IHttpRouteConstraint
    {
        public bool Match(HttpRequestMessage request, IHttpRoute route,
            string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
        {
            object value;
            //TryGetValue是获取指定键相关的值,并返回是否存在值true\false
            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)
        {
            // Web API 配置和服务
            // 将 Web API 配置为仅使用不记名令牌身份验证。
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            // Web API 路由
            DefaultInlineConstraintResolver constraintResolver = new DefaultInlineConstraintResolver();
            constraintResolver.ConstraintMap.Add("MyHttpRouteConstraint", typeof(MyHttpRouteConstraint));
            config.MapHttpAttributeRoutes(constraintResolver);


            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

        }
    }

 控制器代码

 //MyHttpRouteConstraint约束参数不能为0
        [Route("customers/{customerId:MyHttpRouteConstraint}/orders")]
        [HttpGet]
        public IEnumerable<Order> FindOrdersByCustomer(int customerId)
        {
            return DatabaseTest.GetOrdersByCustomerId(customerId);
        }

此时请求结果如图

四、路由优先顺序

 

Route特性设置的路由优先顺序是根据惯例和RouteOrder属性来确定的。

 

惯例是:

1、静态片段变量
2、带约束的片段变量
3、不带约束的片段变量
4、带约束的通配符片段变量
5、不带约束的通配符片段变量

 

RouteOrder属性的默认值是0,属性值越小,排在越前面。

 

[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) { ... }
}

以上,路由的优先顺序是:

orders/details  静态片段变量,RouteOrder属性值为0
orders/{id} 带约束的片段变量,RouteOrder属性值为0
orders/{customerName} 不带约束的片段变量,RouteOrder属性值为0
orders/{*date} 带约束的通配符片段变量,RouteOrder属性值为0
orders/pending RouteOrder属性值为1

 

posted @ 2017-04-13 11:58  Constructor  阅读(824)  评论(0编辑  收藏  举报