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會使用類似的路由模板,如
這時候能夠爲整個controller指定[RoutePrefix]特性,以使用共同的前綴,把[RoutePrefix("/api/books")]加到controller上,action的路由特性就變成這樣:
此外,路由前綴中也能夠包含參數,如[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參數是可選的;也能夠像普通方法那樣指定默認值:
[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() { ... }
不過意義不大,經過順序來控制,還不如設定更好的路由來的實際,並且不至於讓開發人員以爲混亂。