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

 不過意義不大,經過順序來控制,還不如設定更好的路由來的實際,並且不至於讓開發人員以爲混亂。

相關文章
相關標籤/搜索