ASP.NET Web API路由是整個API的入口。我們訪問某個資源就是通過路由映射找到對應資源的URL。通過URL來獲取資源的。
對於ASP.NET Web API內部實現來講,我們的請求最終將定位到一個具體的Action上。所以說,ASP.NET Web API路由就是把客戶端請求映射到對應的Action上的過程。
路由分爲兩種模式:模板路由和特性路由。
1.模板路由
模板路由是ASP.NET Web API默認提供的路由。模板路由使用前需要定義路由模板。如下面默認的路由模板:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace Supernova.Webapi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
我們可以看到此模板的URL格式是api/{controller}/{id}。api代表在資源前面要帶上api目錄,controller代表請求資源的控制器名稱。id代表一條資源的id,id 是可選的。這種默認的模板是不帶action的,所以它是以請求方式來區分資源的,我們必須在action上添加請求方式特性加以區分。
我們添加一個測試控制器api:
public class TestController : ApiController { public object Get1() { return "d1"; } }
我們添加兩個方法如下:
public class TestController : ApiController { public object Get1() { return "d1"; } public object Get2() { return "d2"; } }
錯誤信息:
{"Message":"出現錯誤。","ExceptionMessage":"找到了與該請求匹配的多個操作: \r\n類型 Supernova.Webapi.Controllers.TestController 的 Get1\r\n類型 Supernova.Webapi.Controllers.TestController 的 Get2","ExceptionType":"System.InvalidOperationException","StackTrace":" 在 System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n 在 System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n 在 System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}
我們將代碼改爲如下:
public class TestController : ApiController { public object Get1() { return "d1"; } [HttpPost] public object Get2() { return "d2"; } }
調試返回Get1的信息。
從上面兩個測試我們可以得出如下結論:
action的默認請求方式是HttpGet。
當多個action的 請求方式一樣的話,在默認路由模板下(沒有action),將會匹配多個操作。
基於上面兩點結論,默認路由模板無法滿足針對一種資源一種請求方式的多種操作(比如修改操作,可能針對不同的字段進行修改)。
我們重新定製模板路由,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace Supernova.Webapi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
從上面我們可以看出,在默認路由的基礎上,我們隊路由模板增加了一級action。
測試api如下:
public class TestController : ApiController { public object Get1() { return "d1"; } public object Get2() { return "d2"; } }
我們再通過http://192.168.0.230/api/test訪問,返回404。
我們通過http://192.168.0.230/api/test/Get1訪問,結果正確。
我們通過http://192.168.0.230/api/test/Get2訪問,結果正確。
通過定製路由模板我們可以得出如下結論:
通過在路由模板中增加action目錄,對資源的定位直接作用到action上。
多個HttpGet方法可以共存於一個controller中。
基於上面兩點結論,通過修改路由模板可以滿足針對一種資源一種請求方式的多種操作。
2.特性路由
特性路由是通過給action打attribute的方式定義路由規則。
有時候我們會有這樣的需求,我們請求的一個資源帶有子資源。比如文章評論這樣有關聯關係的資源。我們希望通過如下URL獲得某篇文章下的所有評論:api/book/id/comments。而僅僅憑藉模板路由很難實現這種路由模式。這時候我們就需要特性路由來解決這個問題了。ASP.NET Web API爲我們準備了Route特性,該特性可以直接打到Action上,使用非常靈活、直觀。
我們重新定義api如下:
public class TestController : ApiController { [Route("demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }
請求Get1的URL是http://192.168.0.230/demo
請求Get2的URL是http://192.168.0.230/demo/get
有時候我們想對某個資源的所有操作都加上一個統一的前綴:
[RoutePrefix("api")] public class TestController : ApiController { [Route("demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }
如果我們還會有這樣的需求,我的某個資源中的大部分請求都需要前綴,但是就是有那麼一兩個資源不需要加前綴:
[RoutePrefix("api")] public class TestController : ApiController { [Route("~/demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }
現在問題又來了,那麼多的請求,特別是Get請求方式,都需要帶參數啊,怎麼定義參數的類型,長度範圍等約束條件呢?
答案是可以通過"{參數變量名稱:約束}"來約束路由中的參數變量。
ASP.NET Web API內置約束包括:
{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(正則表達式)}
如下代碼:
[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:int}")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }
以上,如果片段變量id爲int類型,就路由到第一個Action Get1,如果不是,路由到第二個Action Get2。
可以爲一個參數變量同時設置多個約束:
[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:int:min(5)}")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }
請求URL:http://192.168.0.230/api/demo/1 定位到Get2
參考資料:http://www.eggtwo.com/news/detail/155