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


 

之前講了RESTful API的統一資源接口這個約束,里面提到了資源是通過URI來進行識別的,每個資源都有自己的URI。URI里還涉及到資源的名稱,而針對資源的名稱卻沒有一個標准來進行規范,但是業界還是有一些最佳實踐的。那么我們首先看看這些最佳實踐對資源命名是如何建議的。 

 

資源命名 

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

 

使用名詞,而不是動詞 

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

 

需求我們看這樣一個需求的例子:“我想獲得系統里所有的用戶”。 

常見錯誤做法你可能把API的URI設計成這樣:api/getusers。這樣的設計是不好的,因為里面出現了一個動詞get。 

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

正確的做法需求里面主要的動詞應該通過HTTP方法來體現,“獲取”對應的HTTP方法就是GET。而“用戶”這個資源可以用英文user或者users來表示(是否使用復數一直存在爭議,兩種方法都行,但你在使用的時候需要保持一致)。所以正確的uri應該是 GET api/user。 

 

人類能讀懂 

還是上面那個需求:“我想獲得系統里所有的用戶”。 

我們可以把uri設計成 api/u 或者 api/ur。但是這樣設計的話,對API的消費者來說非常的不友好,因為不能直觀的看出來它到底代表的是什么資源,可能是user,也可能是university。 

所以建議的做法是要足夠友好,並且比較簡短,例如: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)。關於路由這部分,下一篇文章再介紹。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM