路由(Route)
Web Api中的路由與Asp.net mvc中的路由基本上一樣,一個路由看起來像是一個URI路徑,但是路由中包含一些大括號包括的占位符(place holder),例如:
api/{Controller}/{Action}/{Id}
當你創建一個路由的時候,你可以為一個或多個占位符設置默認值,例如下面的例子將Controller設為Account,如果請求訪問的URL沒有提供Controller時,將會使用設置的默認的 Controller
defaults:new {Controller=Account}
你還可以為占位符設置一些限制,下面的例子限制Id只能為數字
constraints:new{ id = @"\d+"}
Web Api中的路由主要有三個主要的功能:
- 將請求的URI與路由模板進行匹配
- 選擇Controller
- 選擇Action
當收到請求是,Web Api會嘗試着將請求URI與路由表(Route Table)中注冊的路由模板進行比對,模板中的字面值(Literals)必須被准確的匹配,如上面例子中的api
片段,請求的URL必須包含api
才能匹配上面的路由,而占位符可以匹配任何值(除非你有其它特殊的限制),將請求的URL進行匹配時,只會比對路由模板中有的片段(Segment),而路由模板中沒有的則不會比對,如主機名(host name)、查詢參數(query parameters)等,之后選擇路由表中匹配的第一個路由來進行下一步的處理
Web Api提供的默認路由如下:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
default中設置的id = RouteParameter.Optional
表示路由模板中id是不是必須的,而是可選的,只有在請求的URL存在時才會被賦值
路由字典(Route Dictionary)
如果請求的URL匹配到了與之匹配的路由時,Web api會創建一個Dictionary來存放URL中與路由模板中占位符對應的字段值,這個Dictionary的Key就是路由模板的占位符的名稱(不包括大括號),這些值即可能來自URL,也可能來自注冊路由時設置的默認值。而這個Dictionary就存放IHttpRouteData類型的對象中。
注意 : 如果路由中的占位符被設置為 RouteParameter.Optional,那么這個參數的值(即RouteParameter.Optional)不會被加入到上述的Dictionary中。但如果這個占位符被分配了具體的值的話,這個Dictionary中還是會存儲這個值的,例如:api/products
,那么此時這個Dictionary中存儲的就是:
- controller: "products"
- category:"all"
並不會存儲: id:RouteParameter.Optional,在比如,api/products/toys/123
,那么此時這個Dictionary中存儲的就是:
- controller:"products"
- catagory: "toys"
- id : "123"
這個字典中甚至可以包含路由模板中不存在,但在默認值中存在的值,例如:
routes.MapHttpRoute(
name:"Root",
routeTemplate:"api/root/{id}",
default:new { controller="customers",id = RouteParameter.Optional}
)
如果URL中分離出的片段為api/root/8
,那么此時這個Dictionary中存儲的是:
- controller:"customers
- id : "8"
> #### Controller的選擇
Controller的選擇是通過IHttpControllerSelector.SelectController 的方法來實現的,這個方法接收一個** HttpRequestMessage類型對象返回一個 HttpControllerDescriptor **,默認使用的是Web api內部提供的 DefaultHttpControllerSelector,這個類使用下面的邏輯來選擇Controller:
- 以"Controller"為Key到上述的Dictionary(存儲在IHttpRouteData類型中)查找對應的Value
- 在獲取到的值(即請求的Controller名稱)后面附加"Controller"字符串去獲取對應的Controller的類型名稱
- 拿第二步得到的Controller的名稱去獲取對應的Web Api Controller對象(ApiController類型)
例如,如果路由字典(Route Dictionary)包含鍵值對(key-value pair)"Controller=products",那么控制器的類型便是"ProductsController",如果沒有找到與之匹配的ApiController類型或者有多個匹配,則返回一個錯誤給請求的客戶端。
在第三步中,DefaultHttpControllerSelector 使用 IHttpControllerTypeResolver(典型的IOC應用)接口去獲取所有的ApiController類型(均派生在ApiController),這個接口返回滿足一下條件的所有公共(Public)類型:
- 實現IHttpController接口
- 非抽象類
- 類名以"Controller"結束
> #### Action的選擇
在得到匹配的Controller類型后,Web Api會調用IHttpActionSelector.SelectAction方法去選擇Action,這個方法接收一個HttpControllerContext類型參數,返回一個HttpActionSelector類型實例。選擇Action的大致過程如下:
- 查看請求的Http Method(Get、POST等),獲取請求的Action名稱,方法和獲取Controller名稱的方法一致,以"Action"為Key到路由字典中獲取對應值
- 查看路由表中注冊的路由模板中的默認值
- 查看Controller中定義的Action方法的參數,找出參數最匹配的一個Action
那么,滿足什么條件的方法才能被視為一個Action呢?滿足如下條件的方法便會被認為是Action
- 定義在Controller中的所有公共的實例方法(不包括一些具有特殊名稱的方法,如構造函數、事件、運算符重載等)
Web Api僅僅選擇那些與請求的Http Method匹配的Action:
- 一個Action如果沒有特殊說明,那么它的Http Method為POST
- 你可以通過[HttpGet]、[HttpPost]、[HttpDelete]、[HttpHead]、[HttpOptions]、[HttpPatch]、[HttpPut] 特性來顯式的聲明一個Action的支持的Http Method,也可以通過HttpVerbs特性來聲明。
- 如果一個Action的名稱以Post、Get、Patch、Head、Put、Options、Delete等字符開頭的,則認為該Action支持對應的Http Method。
Action 的參數綁定
Web Api中,Action的參數的創建過程稱為參數的綁定,遵照一下的規則:
- 簡單類型(Simple Type)的參數從URI中獲取其參數值
- 復雜類型(Complex Type)的參數從Http的請求報文中獲取其參數值
那么如何區分簡單類型和復雜類型呢?在Web Api中,如果一個數據類型支持源自字符串的類型轉換,那么該數據對象就是簡單類型,否則,該數據對象就是復雜類型,按照這個判斷的標准,Net中的所有的基元類型(Primative Type)和可空值類型(Nullable Type)、外加 DateTime, Decimal, Guid, String, and TimeSpan.都是簡單類型,而一個自定義的類型默認情況下都是復雜類型.對於一個Action來說,之多有一個參數來自於Http請求報文
注: 在C#中,能夠直接被編譯器識別的類型稱為基元類型.如int、byte等,其分別對應着FCL中的System.Int32和System.Byte
了解了上面的東西后,我們看一下Action 選擇的機制(簡單參數類型)
- 將 Controller中所有滿足請求Http Method的Action放入一個列表中
- 如果路由字典中存在"Action"條目,則從創建好的Action列表中移除名稱不匹配的Action.
- 嘗試着通過URI去獲取Action的參數
- 對於每個Action,獲取其簡單參數類型的參數列表,這其中不包括可選參數(Optional Parameters)
- 試着根據參數名稱從參數列表、路由字典(Route Dictionary)或查詢字符串(Query String)中獲取對應的參數值,這個匹配過程參數名稱不區分大小寫,也不依賴於參數的順序。
- 選擇一個Action,其參數列表中的每個參數在URI都有與之對應的參數值
- 如果有多個Action滿足條件,則選擇一個最為匹配的一個
- 忽略那些應用了[NonAction]特性的Action
> #### 其它
Web Api的路由過程是支持擴展的,當內置的實現邏輯不足以滿足應用的需求時,此時,便可以通過實現相應的接口來對邏輯進行自定義。其中相關的接口及其作用如下:
接口 | 描述 |
---|---|
IHttpControllerSelector | 選擇控制器 |
IHttpControllerResolver | 獲取控制器類型列表,默認的DefaultHttpControllerSelector從 獲取的控制器類型列表中選擇控制器 |
IAssemblyResolver | 獲取項目的程序集列表,IHttpControllerTypeResolver使用這個列表去查找控制器類型 |
IHttpControllerActivator | 創建控制器實例 |
IHttpActionSelector | 選擇Action |
IHttpActionInvoker | 執行Action |
在實現某個接口后,然后使用*HttpConfiguration*類的*Service*集合去注冊后,Web Api便會使用自定義的邏輯去替換掉內置的默認實現。 ```C# var config = GlobalConfiguration.Configuration; config.Services.Replace(typeof(IHttpControllerSelector),new MyControllerSelector(config)); ```