背景與目標
1.背景:由于后續前后端項目重構,改動量較大,接口統一使用Java,規定好前后端協議規范,易于開發和后期維護。
2.本規范的目標:簡潔、統一、開放。
注意本規范是前端組內部制定的接口規范模板,在制定接口規范時候參考,在落地時可根據實際情況作出調整,文檔進行同時進行更新調整,后續前后端就按照這個規范執行。
規范內容
1. 基礎約定
1.1 接口路徑以 /api
或 /[version]/api
開頭
正確:/api/task
或 /v2/api/tasks
錯誤:/biz/tasks
或 /biz/api/tasks
注意:一個頁面/產品
無論后端有多少個服務組成也應該只有一個 API 入口
1.2 接口路徑以 api/aa-bb/cc-dd
方式命名
正確:/api/task-groups
錯誤:/api/taskGroups
1.3 接口路徑使用資源名詞而非動詞,動作應由 HTTP Method 體現,資源組可以進行邏輯嵌套
正確:POST /api/tasks
或 /api/task-groups/1/tasks
表示在 id 為 1 的任務組下創建任務
錯誤:POST /api/create-task
1.4 接口路徑中的資源使用復數而非單數
正確:/api/tasks
錯誤:/api/task
1.5 接口設計面向開放接口,而非單純前端業務
要求:我們在給接口路徑命名時要面向通用業務而非單純前端業務,以獲取篩選表單中的任務字段下拉選項為例
正確:/api/tasks
錯誤:/api/task-select-options
雖然這個接口暫時只用在表單的下拉選擇中,但是需要考慮的是在未來可能會被用在任意場景,因此應以更通用方式提供接口交由客戶端自由組合
1.6 規范使用 HTTP 方法
方法 | 場景 | 例如 |
---|---|---|
GET | 獲取數據 | 獲取單個:GET /api/tasks/1 、獲取列表:GET /api/tasks
|
POST | 創建數據 | 創建單個:POST /api/tasks
|
PATCH | 差量修改數據 | 修改單個:PATCH /api/tasks/1
|
PUT | 全量修改數據 | 修改單個:PUT /api/tasks/1
|
DELETE | 刪除數據 | 刪除單個:DELETE /api/tasks/1
|
其它更多請求方法請查閱 MDN Web Docs
1.7 規范使用 HTTP 狀態碼
狀態碼 | 場景 |
---|---|
200 | 創建成功,通常用在同步操作時 |
202 | 創建成功,通常用在異步操作時,表示請求已接受,但是還沒有處理完成 |
400 | 參數錯誤,通常用在表單參數錯誤 |
401 | 授權錯誤,通常用在 Token 缺失或失效,注意 401 會觸發前端跳轉到登錄頁 |
403 | 操作被拒絕,通常發生在權限不足時,注意此時務必帶上詳細錯誤信息 |
404 | 沒有找到對象,通常發生在使用錯誤的 id 查詢詳情 |
500 | 服務器錯誤 |
其它更多響應狀態碼請查閱 MDN Web Docs
1.8 基礎外層數據結構
-
不分頁數據
{ code: 20000, status: 200, message: "請求成功", data: { id: 1, name: '任務 1' } }
-
分頁數據
{ code: 20000, status: 200, message: "請求成功", data: { items: [{ id: 1, name: '任務 1' }, { id: 2, name: '任務 2' }], total: 2 } }
注意:其中 code
表示業務編碼,status
表示 HTTP 響應狀態碼,如此設計的原因是部分場景下前后端之間存在不可控的網關或代理(比如某些網關有一些流量控制策略會導致直接返回 403 響應狀態碼,此時客戶端無法分辨 403 是網關的還是業務方),類似這類情況下為了能夠讓客戶端正確分辨業務方的真實處理結果則需要在響應體加上 status
,而 code
表示的業務編碼是為了幫助開發者更容易定位問題。
盡管在響應體中體現了響應狀態碼,但這并不代表所有 HTTP 就可以全部返回 200 了,無論如何請在條件允許范圍內盡可能使用標準的 HTTP 響應狀態碼
1.9 請求和響應字段采用 aa_bb_cc
方式命名見名知意
// 正確
{
role_ids: [11,12,35],
}
// 錯誤
{
roleIds: [11, 12, 35],
RoleIds: [11, 12, 35],
ROLE_IDS: [11, 12, 35]
}
1.10 時間字段以 ISO 8601 格式返回 :YYYY-MM-DDTHH:MM:SSZ
1.11 常見業務字段約定
名稱:name
狀態:status
創建時間:created_at
更新時間:updated_at
1.12 空數組使用 [],而不是 null
// 正確
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
role_ids: [],
}
}
// 錯誤
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
role_ids: null
}
}
1.13 前后端傳輸過程以標準 JSON 格式,避免反復正反序列化
// 正確
{
code: 20000,
status: 200,
message: "請求成功",
data: {
roles: [{
id: 1,
name: '角色 1'
}, {
id: 2,
name: '角色 2'
}]
}
}
// 錯誤
{
code: 20000,
status: 200,
message: "請求成功",
data: {
roles: '[{"id":1,"name":"角色 1"},{"id":2,"name":"角色 2"}]'
}
}
2. 創建類接口
2.1 創建完成后直接返回 id
{
code: 20000,
status: 200,
message: "創建成功",
data: {
id: 1,
}
}
2.2 關聯關系只以 id 為標識,其它字段不應依賴客戶端
以創建用戶為例:POST /api/users
// 正確
{
username: 'ming'
password: 'xxxx',
role_ids: [1, 2, 3]
}
// 錯誤
{
username: 'ming'
password: 'xxxx',
role_ids: [{
id: 1,
name: '角色1'
}, {
id: 2,
name: '角色2'
}, {
id: 3,
name: '角色3'
}]
}
2.3 參數錯誤以數組形式返回,并附帶用戶友好的提示
{
code: 40000
status: 400;
message: "參數錯誤",
data: {
errors: [{
field: 'name',
message: '缺失'
}]
}
}
3. 查詢類接口
3.1 排序使用 sort 和 order
例如 GET /api/tasks?sort=created_at&order=descend
表示以創建時間降序查詢數據
注意:其中 order
為 descend
時表示降序,為 ascend
時表示升序
3.2 分頁使用 page 和 per_page
例如 GET /api/tasks?page=1&per_page=10
表示每頁 10 條查詢第一頁數據
注意:其中 page
從 1 開始,而不是 0,如果沒有傳遞 per_page
和 page
參數表示不分頁獲取所有數據
3.3 普通篩選使用鍵值對,多列模糊查詢使用 keyword
關鍵詞,枚舉篩選使用數組合并拼接,區間使用 xxx_lt
和 xxx_gt
關鍵詞
例如 GET /api/tasks?creator=ming
表示查詢所有 ming 用戶創建的任務
例如 GET /api/tasks?keyword=ming
表示查詢任意列包含 ming 關鍵詞的任務
例如 GET /api/tasks?status=pending,complete
表示查詢狀態為阻塞和完成的任務
例如 GET /api/tasks?weight_gt=10&weight_lt=20
表示查詢權重在 10 和 20 之間的任務
例如 GET /api/tasks?weight_gt=10
表示查詢權重大于 10 的任務
3.4 盡可能返回所有關聯數據展開詳情,便于客戶端顯示
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
username: 'ming'
roles: [{
id: 1,
name: '角色 1'
}, {
id: 2,
name: '角色 2'
}, {
id: 3,
name: '角色 3'
}]
}
}
3.5 可枚舉字段使用有語義英文而非無語義數字
// 正確
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
name: '任務 1'
status: 'pending' // 'pending' | 'complete' | 'error'
}
}
// 錯誤
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
name: '任務 1'
status: 0
}
}
3.6 合理自然嵌套結構而不是平鋪
// 正確
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
name: '任務 1'
creator: {
id: 1,
username: '小明'
}
}
}
// 錯誤
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
name: '任務 1'
creator_id: 1,
creator_name: '小明'
}
}
4. 文件類接口
4.1 統一提供單文件上傳接口(/api/files
),支持上傳所有類型文件
// 請求,注意這里是 FormData
{
file: File
}
// 響應
{
code: 20000,
status: 200,
message: "上傳成功",
data: {
id: 'bb313c99',
url: '/files/bb313c99.pdf'
name: '合同.pdf' // 原文件的名稱
}
}
4.2 統一提供多文件上傳接口(/api/multiple-files
),支持上傳所有類型文件
// 請求,注意這里是 FormData
{
files: [File, File]
}
// 響應
{
code: 20000,
status: 200,
message: "上傳成功",
data: [{
id: 'bb313c99',
url: '/files/bb313c99.pdf'
name: '合同1.pdf' // 原文件的名稱
}, {
id: 'bb313c88',
url: '/files/bb313c88.pdf'
name: '合同2.pdf' // 原文件的名稱
}]
}
4.3 文件路徑至少補全至根路徑
// 正確
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
name: '小明'
avatar: '/files/bb313c99.png',
// 或
avatar: 'https://cdn.xxx.com/files/bb313c99.png',
}
}
// 錯誤
{
code: 20000,
status: 200,
message: "請求成功",
data: {
id: 1,
name: '小明'
avatar: 'bb313c99.png'
}
}
4.4 對于使用到文件的接口使用文件 id 或地址而非 FormData
// 正確
{
name: '新任務 1',
file_id: 'bb313c99',
// 或
file_url: '/files/bb313c99.pdf',
}
// 錯誤
{
name: '新任務 1',
file: File
}
注意:先由 POST api/files
上傳完文件拿到文件 id 或地址后再執行后續操作
5. 敏感類接口
5.1 涉及到用戶隱私的應對相關字段做加密處理
// 正確
{
name: '小明',
id_number: 'U2FsdGVkX1+1fW7OpO/tlPXe4IGA/bXExlhKwIR/spk=',
password: 'U2FsdGVkX1/AnXKSBDbztNBfp4czlZxQ++3jRtNZhY0=',
}
// 錯誤
{
name: '小明',
id_number: '310000199511159999',
password: 'ming@xxx.com',
}
注意:敏感字段不要以明文形式展示。