文章出處

問題

怎樣集中的定義路由

 

解決方案

通過調用 HttpRouteCollectionExtension 類中的 MapHttpRoute 擴展方法在 HttpRouteCollection 中定義路由,可以通過 HttpConfiguration 對象調用。

最基礎的使用就是定義一個非常通用的路由模板,他會通過 {controller} 占位符匹配所有的 Controller。如代碼片段 3-1 所示。

 

代碼片段 3-1. ASP.NET WEB API 默認定義的路由以及一個簡單的 Controller

 1 config.Routes.MapHttpRoute(
 2     name: "DefaultApi",
 3     routeTemplate: "api/{controller}/{id}",
 4     defaults: new {id = RouteParameter.Optional}
 5     );
 6 
 7  
 8 
 9 public class OrdersController : ApiController
10 {
11     public Order Get(int id)
12     {
13         // 忽略邏輯
14     }
15 }

 

在路由模板中,可以定義自己的占位符,如代碼片段 3-1 所示的 {id}。他與 Action 中的參數名稱相匹配匹配,ASP.NET WEB API 會從 request 中提取出相應的值,傳入 Action 方法中。也就是,代碼片段 3-1 中 OrdersController 的 Get請求方法的這種情

況。

 

工作原理

從最初版本開始,ASP.NET WEB API 就一直使用集中式路由維護路由表,這和 MVC 如出一轍。

ASP.NET WEB API 定義了很多 MapHttpRoute 的變種。所需參數最少的一個方法,只需要一個路由模板和一個路由名稱。

1 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
2     string routeTemplate)

 

除聲明簡單基礎路由以外的方式,也可以通過默認值和約束,或者設置每個路由消息處理程序,將會在下面的章節介紹這個。所有的這些操作都是通過 Map HttpRoute 的重載方法實現的。

1 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
2     string routeTemplate, object defaults)
3  
4 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
5     string routeTemplate, object defaults, object constraints)
6  
7 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name,
8     string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)

 

通過路由默認值,可以直接定位到路由相關的 Controller;可是,這樣的特定路由需要定義在通用路由之前,也就是代碼片段 3-1 的路由之前。原因就是,路由是順序匹配的,路由在選定處理一個 HTTP 請求的時候,是在每次請求的時候,掃描路由集合中的所有路由,選用第一個被匹配的路由模板。簡單的說,越是特殊的路由定義越靠前,越是通用的路由定義越靠后。

 

在  Self-hosting 中,ASP.NET WEB API 是以 IDictionary<stirng, IHttpRoute> 的形式在維護路由,Idictionary<string, IHttpRoute> 是在 HttpRouteCollection 類中。在 Web host 中也提供了 System.Web.Routing.RouteCollection 的擴展方法,因此,可以直接在這個里面定義 ASP.NET WEB API 路由。

RouteTable.Routes.MapHttpRoute("DefaultApi", "api/{controller}")

 

如果使用 ASP.NET 運行時 host WEB API,無論使用什么擴展方法聲明路由,都會被添加到同一個 RouteTable 中。內部是通過一個叫做 HostedHttpRouteCollection 的類來實現的,這個類是 HttpRouteCollection 的子類,而不是在字典(比如 self-host)中維護路由,所有轉發路由都是查找 System.Web.RouteCollection。

 

ASP.NET WEB API 與 ASP.NET MVC 是不一樣的,API 使用的是基于 HTTP 謂詞匹配來處理一個 Controller 請求。換句話說,框架會根據 HTTP 的東西來選擇一個 Action 處理請求,選擇的邏輯如下:

  • 通過方法名推斷 HTTP 謂詞,如果名字類似于 PostOrder、GetById 等等。
  • 通過 Action 屬性推斷 HTTP 謂詞。
  • Action 參數定義與路由模板必須匹配。

 

典型的 ASP.NET WEB API 路由是指向資源的。可以通過 HTTP  謂詞調用,還需要除了 Action 之外的參數。如代碼片段 3-1 所示的默認路由,可以匹配如下請求:

  • GET myapi.com/api/orders
  • POST myapi.com/api/inovice
  • PUT myapi.com/api/invice/2

 

小提示 在 ASP.NET WEB API 是可以使用 RPC 的,具體細節會在 3-6 介紹。

 

代碼演示

ASP.NET WEB API 是基于 HTTP 謂詞進行分發邏輯的,不像 ASP.NET MVC,很容易出現 AmbiguousMatchEception。例如,可以設想一些使用代碼片段 3-1 的路由的例子。

如代碼片段 3-2 所示的例子,這三個方法都是可以處理相同的 GET 請求,如,/api/orders/1.

代碼片段 3-2.

 

代碼片段 3-2. ASP.NET WEB API Controller

 1 public class BadOrdersController : ApiController
 2 {
 3     [HttpGet]
 4     public Order FindById(int id)
 5     {
 6         // 忽略邏輯
 7     }
 8  
 9     public Order GetById(int id)
10     {
11         // 忽略邏輯
12     }
13  
14     public Order Get(int id)
15     {
16         // 忽略邏輯
17     }
18 }

 

同時我們需要注意,定義復雜、多等級、嵌套路由的時候,集中式路由變得有點麻煩。考慮如下路由

  • GET myapi.com/api/teams
  • GET myapi.com/api/teams/1
  • GET myapi.com/api/teams/1/players

 

使用集中式路由,可以在 Controller 中使用如下三個方法;然而,我們必須注意路由之間沖突的問題,因為有兩個 GET 方法都是只使用了一個 int 的參數。如代碼片段 3-3 所示。有一個特殊的路由指出了 URI 中包含 /players/ 段會被匹配到,而且這個路由定義在通用路由之前。

 

代碼片段 3-3 使用集中式路由配置嵌套路由

 1 public class TeamsController : ApiController
 2 {
 3     public Team GetTeam(int id)
 4     {
 5         // 忽略邏輯
 6     }
 7     public IEnumerable<Team> GetTeams()
 8     {
 9         // 忽略邏輯
10     }
11     public IEnumerable<Player> GetPlayers(int teamId)
12     {
13         // 忽略邏輯
14     }
15 }
16 
17  
18 
19 config.Routes.MapHttpRoute(
20     name: "players",
21     routeTemplate: "api/teams/{teamid}/players",
22     defaults: new {controller = "teams"}
23     );
24 config.Routes.MapHttpRoute(
25 name: "DefaultApi",
26 routeTemplate: "api/{controller}/{id}",
27 defaults: new { id = RouteParameter.Optional }
28 );

 

集中式路由的主要問題是,特殊路由的定義,僅僅是處理特殊的 Controller 特殊的 Action。通過定義特殊路由并添加到一般路由之前,在人為干預下短路了路由匹配。

 

這種處理并不是最理想,當應用程序變大之后,會有更復雜的、多層級的路由,可能就要在集中式路由中痛苦的掙扎(路由維護和調試)。更好的選擇就是使用直接式路由,下一篇 3-2 定義直接路由,以及后面介紹路由的時候都會涉及直接式路由。

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()