使用ASP.NET Core 3.x 構建 RESTful API - 3.1 資源命名

 

以前講了RESTful API的統一資源接口這個約束,裏面提到了資源是經過URI來進行識別的,每一個資源都有本身的URI。URI裏還涉及到資源的名稱,而針對資源的名稱卻沒有一個標準來進行規範,可是業界仍是有一些最佳實踐的。那麼咱們首先看看這些最佳實踐對資源命名是如何建議的。 web

 

資源命名 

下面讓咱們來看看RESTful API資源命名的一些最佳實踐。 json

 

使用名詞,而不是動詞 

一個資源的URI表明的是一個實際上或概念上存在的東西,所以,它應該是名詞,因此也就不該該出現動詞,動詞應該使用HTTP方法來表達。 後端

 

需求咱們看這樣一個需求的例子:「我想得到系統裏全部的用戶」。 api

常見錯誤作法你可能把API的URI設計成這樣:api/getusers。這樣的設計是很差的,由於裏面出現了一個動詞get。 mvc

分析這個句話的主要動詞就是「獲取」,而想要獲取的資源(也就是主要的名詞)是「用戶」。 async

正確的作法需求裏面主要的動詞應該經過HTTP方法來體現,「獲取」對應的HTTP方法就是GET。而「用戶」這個資源能夠用英文user或者users來表示(是否使用複數一直存在爭議,兩種方法都行,但你在使用的時候須要保持一致)。因此正確的uri應該是 GET api/user。 函數

 

人類能讀懂 

仍是上面那個需求:「我想得到系統裏全部的用戶」。 ui

咱們能夠把uri設計成 api/u 或者 api/ur。可是這樣設計的話,對API的消費者來講很是的不友好,由於不能直觀的看出來它到底表明的是什麼資源,多是user,也多是university。 spa

因此建議的作法是要足夠友好,而且比較簡短,例如:api/users 設計

 

要體現資源的結構關係 

假設若是後端API系統裏面有若干種資源,而用戶這個資源與其它的資源並無直接的關係,這樣的話獲取用戶資源的uri應該是 api/users。而不是 api/products/users,也不是api/catalogs/products/users,由於user和product或者catalog沒有直接的關係。 

經過id獲取單個用戶的uri應該是:api/users/{userId},而不是api/userid/users。 

這樣寫的好處是可讓API具備很好的可預測性和一致性。 

 

需求1系統裏有兩類資源,公司(Company)和員工(Employee),它們倆是包含關係,也就是一個公司包含多個員工。如今我想獲取某個公司下全部的員工信息。 

分析這裏的主要動詞仍是「獲取」,因此咱們可使用HTTP的GET。而這裏的資源有兩個,分別是公司和員工,並且它們是包含關係:一個公司包含多個員工或者說一個公司是一個員工的集合。因此API的URI在設計的時候須要體現這種包含關係。 

常見的錯誤作法若是你想得到公司這個資源,我想你如今應該不會出錯,uri應該是 api/companies。而想要獲取某個公司下的員工,常見的錯誤作法有:api/employees,api/employees/{companyId}等等。這些設計很是很差是由於它沒法體現出Company和Employee之間的結構關係。 

建議的作法:須要體現Company和Employee之間的關係,因此uri應該是GET api/companies/{companyId}/employees。這樣作直接體現出了Company和Employee之間的結構關係,並且也體現出了一個Company就是一個Employee的集合體。 

 

需求2:我想獲取某個公司的某個員工信息。 

常見的錯誤作法:api/employees/{employeeId},api/companies/{employeeId}等等。這些作法都沒法體現出Company和Employee之間的關係。 

建議的作法:api/companies/{companyId}/employees/{employeeId} 

 

自定義查詢怎麼命名 

咱們常常會遇到這樣的需求,好比獲取按照某個資源排序後的資源,或者按照某些條件過濾後的資源。這時候應該怎對資源進行命名呢? 

 

需求:「我想獲取全部的用戶信息,並要求結果是按年齡從小到大進行排列的」。 

常見錯誤的作法:api/users/orderby/age。以前說了,uri裏面使用的都應該是名詞,若是按照這個uri的結構來看,那麼orderby和age就應該是另外兩個資源,而且users包含orderby,orderby包含age,這顯然是錯誤的。 

建議的作法:api/users?orderby=name,這樣設計更合理一些。這裏使用了query string做爲查詢參數進行排序。 

 

例外 

有一些需求老是沒法知足的達到RESTful的約束。 

需求:「我想獲取系統裏全部用戶的數量」。 

妥協的作法:咱們確實能夠先經過 GET api/users來獲取系統裏全部的用戶信息,而後再算出用戶的數量,可是這樣作也太浪費資源而且效率也過低了。咱們也很難使用某個名詞來表示這個需求的資源。例如:api/users/totalamountofuser。這樣的uri按理說就表明着咱們將會獲取到一個集合資源,裏面是一堆數字,但針對這個需求,我也沒有特別好的辦法讓uri命名徹底符合RESTful的約束,因此針對這個需求,我使用的就是這個uri。 

 

Demo

下面咱們就來實踐一下。打開以前的項目,並創建CompaniesController 

ApiController] 
I reference 
public class CompaniesController 
ControllerBase 
private readonly ICompanyRepository 
companyRepository; 
O references 
public CompaniesController(ICompanyRepository companyRepository) 
companyRepository - 
companyRepository ?? 
throw new ArgumentNullException(nameof(companyRepository)); 
O references 
public async Task<IActionResult> GetCompanies() 
await companyRepository .GetCompaniesAsync() ; 
var companies 
return new JsonResult(companies);

這裏有6個地方比較關鍵,咱們挨個看一下: 

  1. RESTful API 或者其它Web APIController都應該繼承於 ControllerBase 這個類(點此查看詳細的官方文檔),而不是Controller這個類。 

    1. Controller類繼承於ControllerBaseController添加了對視圖的支持,所以它更適合用於處理 MVC Web 頁面,而不是 Web API。可是若是你的Controller須要同時支持MVC Web頁面和Web API,那麼這時候就應該繼承於Controller這個類。 

    2. ControllerBase 類提供了不少用於處理 HTTP 請求的屬性和方法。 例如,ControllerBase.CreatedAtAction 返回 201 狀態代碼。關於ControllerBase的屬性和方法的詳細列表,請查看官方參考文檔 

  2. [ApiController]。這個屬性是應用於Controller的,它其實並非強制的,可是它提供了一些幫助,使得Web API的開發體驗更好。詳細教程請點擊 [ApiController]的官方文檔。在Controller上面添加了[ApiController]屬性以後,就會啓用如下行爲: 

    1. 要求使用屬性路由(Attribute Routing)。也就是不能經過StartupConfigure方法統一配置路由模板。這部分的詳細介紹請點擊:官方文檔 

    2. 自動HTTP 400響應。也就是Action方法傳入的model含有驗證錯誤的時候,自動觸發HTTP 400響應。這部分的詳細介紹請點擊:官方文檔 

    3. 推斷參數的綁定源。它將會推斷出Action方法的參數到底來自哪一個綁定源,例如[FromBody][FromForm]等等。這部分的詳細介紹請點擊:官方文檔 

    4. Multipart/form-data 請求推斷使用 [FromForm] 屬性批註操做參數時,[ApiController] 屬性應用推斷規則,它會推斷 multipart/form-data 請求內容類型。這部分的詳細介紹請點擊:官方文檔 

    5. 錯誤狀態代碼的問題詳細信息MVC 會將錯誤結果(狀態代碼爲 400 或更高的結果)轉換爲狀態代碼爲 ProblemDetails 的結果。 ProblemDetails 類型基於 RFC 7807 規範,用於提供 HTTP 響應中計算機可讀的錯誤詳細信息。這部分的詳細介紹請點擊:官方文檔 

  3. 咱們須要經過構造函數注入ICompanyRepository,並把它存放在一個只讀的字段裏面。 

  4. 若是注入的ICompanyRepository的實例爲null,那麼就拋出一個ArgumentNullException 

  5. 想要返回數據結果,咱們須要在Controller裏面添加一個Action方法。我暫時把它的返回類型寫爲IActionResult(詳細介紹請點擊官方文檔)。IActionResult裏面定義了一些合約,它們能夠表明Action方法返回的結果。 

  6. 我暫時只想把結果序列化爲JSON格式並返回,這裏我new了一個JsonResult參考文檔),它能夠作這項工做。 

 

目前我只作了這幾項最基本的工做:建立Controller,注入Repository,建立Action方法並返回結果。下面運行一下看看報了什麼錯: 

這是由於GetCompanies這個Action方法並無使用屬性路由(Attribute Routing)。關於路由這部分,下一篇文章再介紹。

相關文章
相關標籤/搜索