WebApi深刻学习--特性路由

特性路由

WebApi2默认的路由规则咱们称做基于约定路由,不少时候咱们使用RESTful风格的URI.简单的路由是没问题的,如 api/Products/{id},但有些事很难处理的,如资源之间存在嵌套关系:客户包含订单,书有做者属性等等。对于这种Uri,咱们但愿的路由是这样的:/costomers/{customerid}/orders 或 /costomers/{customerid}/orders/{orderid}正则表达式

考虑到这只是某个Controller的路由格式,而咱们会有不少个Controller,用基于约定路由显然不合适(要配置不少的路由)api

使用特性路由就简单了,在action上加一个特性便可ui

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }

 经过使用特性路由,咱们还能够作API的版本控制spa

/api/v1/products
/api/v2/products版本控制

 启用特性路由须要在配置过程当中调用System.Web.HttpConfigurationExtensions类的MapHttpAttributeRoutes方法code

using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}

 

在WebApi1中  项目模板是这样的blog

protected void Application_Start()
{
    WebApiConfig.Register(GlobalConfiguration.Configuration);
    //。。。
}

若是要启用特性路由,须要改为以下代码接口

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
    //。。。
}

 :特性路由和基于约定路由是能够结合使用大的。ci

 HttpMethod资源

默认状况,WebApi会根据action的方法名前缀查找action(不区分大小写),好比GetUsers,会匹配Get。经过在action上添加HttpMethod特性,能够覆盖action须要映射的Http Method。

可以使用的特性包括:[HttpDelete],[HttpPost],[HttpHead],[HttpOptions],[HttpPatch],[HttpGet],[HttpPut]

经过AcceptVerbs特性,咱们还能够指定非标准方法以及多个方法,如 [AcceptVerbs("MKCOL","GET","POST")]

 

路由前缀

一般状况下,一个Controller下的action会使用类似的路由模板,如

  • [Route("api/books")]
  • [Route("api/books/{id:int}")]
  • [Route("api/books/{bookid}/authors")]

这时候能够为整个controller指定[RoutePrefix]特性,以使用共同的前缀,把[RoutePrefix("/api/books")]加到controller上,action的路由特性就变成这样:

  • [Route("")]
  • [Route("{id:int}")]
  • [Route("{bookid}/authors")]

此外,路由前缀中也能够包含参数,如[RoutePrefix("api/{userid}/books")]

这里还有两个小技巧

若是有某个特殊路由不但愿使用前缀,能够在路由中添加~,如[Route("~api/otherbooks")]

有时候须要几个路由片断结合起做用,如日期 /api/books/date/2013/06/17

这时候就须要使用字符* ,[Route("date/{*date:datetime:regex(\\d{4}/\\d{2}/\\d{2})}")],不过这种参数只能用做路由的最后一个参数 

 

路由约束

路由约束让咱们能够限制模板参数的匹配方式。通常的语法是 "{参数:约束类型}":

[Route("users/{id:int}"]
public User GetUserById(int id) { ... }

[Route("users/{name}"]
public User GetUserByName(string name) { ... }

 若是参数int,则选中第一个GetUserById,不然是GetUserByName。(跟方法定义的顺序无关)

下面的表格列出了支持的约束

约束 介绍 示例
alpha 匹配大写或小写字母 (a-z, A-Z) {x:alpha}
bool   {x:bool}
datetime   {x:datetime}
decimal   {x:decimal}
double   {x:double}
float 匹配一个 32位浮点数 {x:float}
guid   {x:guid}
int   {x:int}
length 匹配一个长度在指定范围内的字符串 {x:length(6)}
{x:length(1,20)}
long   {x:long}
max 匹配指定了最大值的整数 {x:max(10)}
maxlength 匹配指定了最大长度字符串 {x:maxlength(10)}
min 匹配指定了最小值的整数 {x:min(10)}
minlength 匹配指定了最小长度字符串 {x:minlength(10)}
range 匹配指定了大小区间的整数 {x:range(10,50)}
regex 匹配一个正则表达式 {x:regex(^\d{3}-\d{3}-\d{4}$)}

 若是要指定多个约束,须要用冒号间隔 [Route("users/{id:int:min(1)}")]

经过实现IHttpRouteConstraint接口,还能够建立自定义路由约束。(不过通常正则就能够搞定了)

还能够经过实现IInlineConstraintResolver接口替换整个DefaultInlineConstraintResolver类。这样作将取代全部的内置的约束,除非实现IInlineConstraintResolver的类将它们添加进去。

public class NonZeroConstraint : IHttpRouteConstraint
{
    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
        IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            long longValue;
            if (value is long)
            {
                longValue = (long)value;
                return longValue != 0;
            }
        }
        return false;
    }
}

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var constraintResolver = new DefaultInlineConstraintResolver();
        constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));

        config.MapHttpAttributeRoutes(constraintResolver);
    }
}

[Route("{id:nonzero}")]
public HttpResponseMessage GetNonZero(int id) { ... }

  

可选URI参数,默认值

 经过在参数约束后面添加一个问号,能够设定URI参数是可选的;也能够像普通方法那样指定默认值:

[Route("api/books/locale/{lcid:int?}")]
public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
[Route("api/books/locale/{lcid:int=1033}")]
public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }

  这二者是等价的

 

路由名称

WebApi中,每个路由都有一个名字,用于生成连接,并在放入Http响应中。(应该是用于重定向吧) 

例如对某个action A指定Name,[Route("api/books/{id}", Name="GetBookById")]

那么其余action B在须要返回这个action A的连接时,就能够这样使用

public HttpResponseMessage Post(Book book)
{
    var response = Request.CreateResponse(HttpStatusCode.Created);
    string uri = Url.Link("GetBookById", new { id = book.BookId });
    response.Headers.Location = new Uri(uri);
    return response;
}

 路由顺序

经过设定特性[Route("xxx",RouteOrder=n)]能够指定路由的查找顺序

[Route("pending", RouteOrder = 1)]
public HttpResponseMessage GetPending() { ... }

 不过意义不大,经过顺序来控制,还不如设定更好的路由来的实际,并且不至于让开发人员以为混乱。

相关文章
相关标签/搜索