一、WebApi路由機制是什麼? 路由機制通俗點來說:其實就是WebApi框架將用戶在瀏覽器中輸入的Url地址和路由表中的路由進行匹配,並根據最終匹配的路由去尋找並匹配相應的Controller和Action並執行的一個過程。 從WebApi框架接收到來自外部環境的介面調用請求到指定介面的執行大概需 ...
一、WebApi路由機制是什麼?
路由機制通俗點來說:其實就是WebApi框架將用戶在瀏覽器中輸入的Url地址和路由表中的路由進行匹配,並根據最終匹配的路由去尋找並匹配相應的Controller和Action並執行的一個過程。
從WebApi框架接收到來自外部環境的介面調用請求到指定介面的執行大概需要以下的匹配過程:1、匹配URL路由 2、Controller匹配 3、Action匹配
下麵我麽分別對這幾個流程進行詳細說明。
二、匹配URL路由
WebApi框架接收到來自外部的介面請求後,首先將路由表中的路由一條一條和Url地址進行匹配,一旦匹配上則不再繼續往下匹配,如果匹配完了所有路由都未匹配上,則會報404錯誤。
如果成功匹配上指定路由,則框架根據路由的Url模板中指定的Controller的所在位置以及Action的所在位置從用戶請求的Url中提取出將要調用的Controller的名稱以及Action的名稱,當然要被調用的Controller名或者Action名也可能不是從用戶發起的介面調用請求Url中獲得的,因為WebApi中在進行路由配置時提供了參數預設值的配置,也就是說Controller名或者Action名可能來源於路由配置時的預設值。
該流程結束後:WebApi框架將會從用戶請求的介面調用URL中提取出用戶想調用的介面所對應的Controller 、Action 、以及用戶傳給指定Action的參數(即路由數據)等等,
這些數據存儲在了一個字典集合中,這個路由數據字典我們可以在Action方法中通過如下方式獲得:
1 IHttpRouteData routeData = Request.GetRouteData(); 2 IDictionary<string, object> routeDataValues = routeData.Values;
這裡需要澄清的幾點的是:
1、URL未匹配上任何路由 和 匹配上了路由但是未找到相應的Controller和Action 是兩個不同的概念,IIS對這兩種情況的響應是不一樣的。
URL未匹配上路由:
如果用戶請求的介面的地址不能和路由表中的所有路由相匹配,IIS將直接報告404錯誤。
匹配上了路由但未找到相應Controller或Action:
如果匹配上了路由但是未找到響應的Controller或Action,那麼將報類似如下錯誤:
MessageDetail節點詳細描述了是 Controller未找到還是 Action未找到。
2、由於路由表中可以配置一條或者多條路由,並且WebApi框架在匹配成功一條路由後將不再繼續往下匹配,也就是說即使此時後面還有路由可以
和當前請求匹配也不會被匹配到,所以請務必註意每條路由的配置順序,否則可能造成意想不到的結果。
如:
1 public static void Register(HttpConfiguration config) 2 { 3 config.Routes.MapHttpRoute( 4 name: "TestRoute", 5 routeTemplate: "{controller}/{action}", 6 defaults: new { action = "Index" } 7 ); 8 config.Routes.MapHttpRoute( 9 name: "DefaultApi", 10 routeTemplate: "api/{controller}/{id}", 11 defaults: new { id = RouteParameter.Optional } 12 ); 13 14 }
假設我們此時代碼中包含如下控制器和Action:
1 public class ValuesController : ApiController 2 { 3 public string Get() 4 { 5 return "value"; 6 } 7 [HttpGet] 8 public object QueryValues(int index) 9 { 10 string[] strs = new string[] { "張三", "李四" }; 11 if (index < 0 || index >= strs.Length) 12 { 13 return JsonConvert.SerializeObject(strs); 14 } 15 return strs[index]; 16 } 17 }
我們在瀏覽器中輸入:http://localhost:16982/api/Values,會發現總是匹配到的是TestRoute這個路由,實際上我們只是想訪問Values中的無參數的Get方法。
事實上,解決這個問題只需要將TestRoute路由的配置放到DefaultApi路由的後面即可。
三、Controller匹配
經過路由匹配後,框架已經從用戶請求的Url中提取到需要訪問的Controller名、Action名、以及在路由模板中定義的參數所對應的值等等,此時WebApi框架從路由數據字典中獲取出鍵為:controller的值,在這個值的基礎上去找類型名為 Controller名+Controller 結尾的類,此時如果找到相應名稱的Controller類,則Controller匹配成功,但此時僅僅是Controller匹配成功,最終介面能否成功調用還需取決於下一個步驟中的Action匹配。
Controller的匹配主要由介面:IHttpControllerSelector.SelectController方法來處理的,這個是WebApi框架定義的介面,定義如下:
1 public interface IHttpControllerSelector 2 { 3 // Methods 4 IDictionary<string, HttpControllerDescriptor> GetControllerMapping(); 5 HttpControllerDescriptor SelectController(HttpRequestMessage request); 6 }
WebApi框架對控制器的匹配進行了預設的實現,類名叫做:DefaultHttpControllerSelector,這個類預設實現了查找Controller的過程,在DefaultHttpControllerSelector內部通過
IHttpControllerTypeResolver介面載入出所有的滿足條件的控制器類型,能被載入並查找的控制器類型必須滿足以下條件:
1、類必須是實現了IHttpController介面
2、必須是public
3、不能是abstract類
4、類名必須以Controller結尾
最終,從這些列表中找出名稱和路由數據中的Controller名同名的Controller類,並創建該類的實例對象。
當然如果我們需要有自己的控制器匹配的邏輯,我們也可以對其進行配置,通過在:/App_Start/WebApiConfig.cs類中進行配置,配置方式如下所示:
1 public static class WebApiConfig 2 { 3 public static void Register(HttpConfiguration config) 4 { 5 // 這裡我們配置成使用自己寫的匹配控制器的邏輯 6 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new MyHttpControllerSelectory(GlobalConfiguration.Configuration)); 7 } 8 }
雖然WebApi框架給我們提供了匹配控制器行為的擴展點,但是微軟的預設實現類DefaultHttpControllerSelector基本上可以滿足大多數場景了。
四、Action匹配
路由機制找到了將要被調用的Controller類後,接下來就是在當前已經被匹配成功的控制器類下找一個合適的Action方法,並對其調用了,Action的具體匹配流程如下:
1、根據用戶調用介面時的請求方式(GET/POST/DELETE/PUT/Head等等)從已經匹配的控制器類中查找是用於該種請求方式的Action方法,通過該輪匹配可能會匹配出多個符合條件的Action方法。
2、如果路由數據字典中包含鍵為:action的值,那麼表示Action的名稱必須和該字典中的actionName相一致,也就是說只有Action方法的名稱和路由數據字典中的action名匹配的才算再次步驟匹配。(該步驟不一定是必須執行的,取決於被匹配的路由中是否有指定action占位符)
3、最後一步就是action的參數綁定了,action中的各個參數的值要麼來源於路由Url模板中定義好的參數在Url中提取到的值,要麼來源於QueryString(也就是?後面的參數值),當然這些說的只是.NET中的原生類型(包括:int/double/DateTime/TimeSpan/Guid等等)的綁定。並不包括自定義的複雜類型(如模型類),其實複雜類型的參數值的綁定預設實現方式是從請求報文提中獲得的。
Action的選擇由介面IHttpActionSelector.SelectAction()方法進行實現,WebApi框架對Action的匹配進行了預設的實現,預設實現類名為:ApiControllerActionSelector,
IHttpActionSelector介面定義如下:
1 public interface IHttpActionSelector 2 { 3 // Methods 4 ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor); 5 HttpActionDescriptor SelectAction(HttpControllerContext controllerContext); 6 }
需要說明的是:能作為Action被執行的方法必須滿足以下幾點:
1、必須是public修飾的方法
2、未被[NonAction]特性修飾的方法
3、不是從ApiController類中繼承過來的方法
4、控制器的構造函數,等等也不會被匹配。
Action的匹配我們也可以實現自定義匹配規則,和上面提到的自定義Controller匹配規則的配置方式類似,如下:
1 public static void Register(HttpConfiguration config) 2 { 3 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionSelector), new MyApiControllerActionSelector()); 4 5 }
本文主要講述了WebApi框架如何將來自用戶的介面調用請求映射到具體的Controller和Action,並對其進行執行的過程,最容易讓人困惑的部分或許還是Action的選擇部分,
後續我們將繼續討論關於Action選擇部分的具體細節以及參數綁定過程。