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