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