前后端接口規范

背景與目標

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 表示以創建時間降序查詢數據

注意:其中 orderdescend 時表示降序,為 ascend 時表示升序

3.2 分頁使用 page 和 per_page

例如 GET /api/tasks?page=1&per_page=10 表示每頁 10 條查詢第一頁數據

注意:其中 page 從 1 開始,而不是 0,如果沒有傳遞 per_pagepage 參數表示不分頁獲取所有數據

3.3 普通篩選使用鍵值對,多列模糊查詢使用 keyword 關鍵詞,枚舉篩選使用數組合并拼接,區間使用 xxx_ltxxx_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',
}

注意:敏感字段不要以明文形式展示。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容