RESTful規範Api最佳設計實踐
RESTful
是目前比較流行的介面路徑設計規範,基於HTTP,一般使用JSON方式定義,通過不同HttpMethod來定義對應介面的資源動作,如:新增(POST)、刪除(DELETE)、更新(PUT、PATCH)、查詢(GET)等。
路徑設計
在RESTful
設計規範內,每一個介面被認為是一個資源請求,下面我們針對每一種資源型別來看下API路徑設計。
路徑設計
的注意事項
如下所示:
- 資源名使用複數
- 資源名使用名詞
- 路徑內不帶特殊字元
- 避免多級URL
新增資源
請求方式 | 示例路徑 |
---|---|
POST | https://api.yuqiyu.com/v1/users |
新增資源使用POST
方式來定義介面,新增資源資料通過RequestBody
curl -X POST -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users -d '{
"name": "恆宇少年",
"age": 25,
"address": "山東濟南"
}'
新增資源後接口應該返回該資源的唯一標識,比如:主鍵值。
{ "id" : 1, "name" : "恆宇少年" }
通過返回的唯一標識來操作該資源的其他資料介面。
刪除資源
請求方式 | 示例路徑 | 備註 |
---|---|---|
DELETE | https://api.yuqiyu.com/v1/users | 批量刪除資源 |
DELETE | https://api.yuqiyu.com/v1/users/{id} | 刪除單個資源 |
刪除資源使用DELETE
方式來定義介面。
-
根據主鍵值刪除單個資源
curl -X DELETE https://api.yuqiyu.com/v1/users/1
將資源的
主鍵值
通過路徑的方式傳遞給介面。 -
刪除多個資源
curl -X DELETE -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users -d '{ "userIds": [ 1, 2, 3 ] }'
刪除多個資源時通過
RequestBody
方式進行傳遞刪除條件的資料列表,上面示例中通過資源的主鍵值集合作為刪除條件,當然也可以通過資源的其他元素作為刪除的條件,比如:name
更新資源
請求方式 | 示例路徑 | 備註 |
---|---|---|
PUT | https://api.yuqiyu.com/v1/users/{id} | 更新單個資源的全部元素 |
PATCH | https://api.yuqiyu.com/v1/users/{id} | 更新單個資源的部分元素 |
在更新資源資料時使用PUT
方式比較多,也是比較常見的,如下所示:
curl -X PUT -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users/1 -d '{
"name": "恆宇少年",
"age": 25,
"address": "山東濟南"
}'
查詢單個資源
請求方式 | 示例路徑 | 備註 |
---|---|---|
GET | https://api.yuqiyu.com/v1/users/{id} | 查詢單個資源 |
GET | https://api.yuqiyu.com/v1/users?name={name} | 非唯一標識查詢資源 |
-
唯一標識查詢單個資源
curl https://api.yuqiyu.com/v1/users/1
通過唯一標識查詢資源時,使用路徑方式傳遞標識值,體現出層級關係。
-
非唯一標識查詢單個資源
curl https://api.yuqiyu.com/v1/users?name=恆宇少年
查詢資源資料時不僅僅都是通過唯一標識值作為查詢條件,也可能會使用資源物件內的某一個元素作為查詢條件。
分頁查詢資源
請求方式 | 示例路徑 |
---|---|
GET | https://api.yuqiyu.com/v1/users?page=1&size=20 |
分頁查詢資源時,我們一般需要傳遞兩個引數作為分頁的條件,page
代表了當前分頁的頁碼,size
則代表了每頁查詢的資源數量。
curl https://api.yuqiyu.com/v1/users?page=1&size=20
如果分頁時需要傳遞查詢條件,可以繼續追加請求引數。
https://api.yuqiyu.com/v1/users?page=1&size=20&name=恆宇少年
動作資源
有時我們需要有動作性的修改某一個資源的元素內容,比如:重置密碼。
請求方式 | 示例路徑 | 備註 |
---|---|---|
POST | https://api.yuqiyu.com/v1/users/{id}/actions/forget-password | - |
使用者的唯一標識在請求路徑中進行傳遞,而修改後的密碼通過RequestBody
方式進行傳遞,如下所示:
curl -X POST -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users/1/actions/forget-password -d '{
"newPassword": "123456"
}'
版本號
版本號是用於區分Api
介面的新老標準,比較流行的分別是介面路徑
、頭資訊
這兩種方式傳遞。
-
介面路徑方式
我們在部署介面時約定不同版本的請求使用
HTTP代理
轉發到對應版本的介面閘道器,常用的請求轉發代理比如使用:Nginx
等。這種方式存在一個弊端,如果多個版本同時將請求轉發到同一個
閘道器
時,會導致具體版本的請求轉發失敗,我們訪問v1
時可能會轉發到v2
,這並不是我們期望的結果,當然可以在閘道器
新增一層攔截器,通過提取路徑上班的版本號來進行控制轉發。# v1版本的請求 curl https://api.yuqiyu.com/v1/users/1 # v2版本的請求 curl https://api.yuqiyu.com/v2/users/1
-
頭資訊方式
我們可以將訪問的介面版本通過
HttpHeader
的方式進行傳遞,在閘道器
根據提取到的頭資訊進行控制轉發到對應版本的服務,這種方式資源路徑的展現形式不會因為版本的不同而變化。# v1版本的請求 curl -H 'Accept-Version:v1' https://api.yuqiyu.com/users/1 # v2版本的請求 curl -H 'Access-Version: v2' https://api.yuqiyu.com/users/1
這兩個版本的請求可能請求引數、返回值都不一樣,但是請求的路徑是一樣的。
版本頭資訊的
Key
可以根據自身情況進行定義,推薦使用Accpet
形式,詳見<a href="http://www.informit.com/articles/article.aspx?p=1566460" target="_blank">Versioning REST Services</a>。
狀態碼
在RESTful
設計規範內我們需要充分的裡面HttpStatus
請求的狀態碼來判斷一個請求傳送狀態,本次請求是否有效,常見的HttpStatus
狀態碼如下所示:
狀態碼 | 發生場景 |
---|---|
200 | 請求成功 |
201 | 新資源建立成功 |
204 | 沒有任何內容返回 |
400 | 傳遞的引數格式不正確 |
401 | 沒有許可權訪問 |
403 | 資源受保護 |
404 | 訪問的路徑不正確 |
405 | 訪問方式不正確,GET請求使用POST方式訪問 |
410 | 地址已經被轉移,不可用 |
415 | 要求介面返回的格式不正確,比如:客戶端需要JSON格式,介面返回的是XML |
429 | 客戶端請求次數超過限額 |
500 | 訪問的接口出現系統異常 |
503 | 服務不可用,服務一般處於維護狀態。 |
針對不同的狀態碼我們要做出不同的反饋,下面我們先來看一個常見的引數異常
錯誤響應設計方式:
# 發起請求
curl -X POST -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users -d '{
"name": "",
"age": 25,
"address": "山東濟南"
}'
# 響應狀態
HttpStatus 200
# 響應內容
{
"code": "400",
"message": "使用者名稱必填."
}
在服務端我們可以控制不同狀態碼、不同異常的固定返回格式,不應該將所有的異常請求都返回200
,然後對應返回錯誤,正確的方式:
# 發起請求
curl -X POST -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users -d '{
"name": "",
"age": 25,
"address": "山東濟南"
}'
# 響應狀態
HttpStatus 400
# 響應內容
{
"error": "Bad Request",
"message": "使用者名稱必填."
}
響應格式
介面的響應格式應該統一
。
每一個請求成功的介面返回值外層格式應該統一,在服務端可以採用實體方式進行泛型返回。
如下所示:
/**
* Api統一響應實體
* {@link #data } 每個不同的介面響應的資料內容
* {@link #code } 業務異常響應狀態碼
* {@link #errorMsg} 業務異常訊息內容
* {@link #timestamp} 介面響應的時間戳
*
* @author 恆宇少年 - 於起宇
*/
@Data
public class ApiResponse<T> implements Serializable {
private T data;
private String code;
private String errorMsg;
private Long timestamp;
}
-
data
由於每一個
API
的響應資料型別不一致,所以在上面採用的泛型的泛型進行返回,data
可以返回任意型別的資料。 -
code
業務邏輯異常碼,比如:USER_NOT_FOUND(使用者不存在)這是介面的約定
-
errorMsg
對應
code
值得描述。 -
timestamp
請求響應的時間戳
總結
RESTful
是API
的設計規範,並不是所有的介面都應該遵循這一套規範來設計,不過我們在設計初期更應該規範性,這樣我們在後期閱讀程式碼時根據路徑以及請求方式就可以瞭解介面的主