RESTful API 設計規範

轉載來自:http://www.javashuo.com/article/p-zbtovhub-t.htmlphp

本文是爲 騰訊大渝網 API開發規範擬定的一個beta版,文章大量參考了目前比較常見的RESETful API設計。html

爲了更好的討論規範帶來的爭議及問題,現已把該文檔整理並開源到 github前端

關於「能願動詞」的使用

爲了不歧義,文檔大量使用了「能願動詞」,對應的解釋以下:nginx

  •  必須 (MUST):絕對,嚴格遵循,請照作,無條件遵照;
  •  必定不可 (MUST NOT):禁令,嚴令禁止;
  •  應該 (SHOULD):強烈建議這樣作,可是不強求;
  •  不應 (SHOULD NOT):強烈不建議這樣作,可是不強求;
  •  能夠 (MAY)和可選 (OPTIONAL):選擇性高一點,在這個文檔內,此詞語使用較少;

協議

在經過API於後端服務通訊的過程當中,應該使用HTTPS協議。git

API Root URL

API的根入口點應儘量保持足夠簡單,這裏有兩個常見的URL根例子:github

  •  api.example.com/*
  •  example.com/api/*

若是你的應用很龐大或者你預計它將會變的很龐大,那應該將API放到子域下。這種作法能夠保持某些規模化上的靈活性。json

Versioning

全部的API必須保持向後兼容,你必須在引入新版本API的同時確保舊版本API仍然可用。因此應該爲其提供版本支持。後端

目前比較常見的兩種版本號形式:api

在 URL 中嵌入版本編號

api.example.com/v1/* 

這種作法是版本號直觀、易於調試;另外一種作法是,將版本號放在HTTP Header頭中:服務器

經過媒體類型來指定版本信息

Accept: application/vnd.example.com.v1+json 

其中vnd表示Standards Tree標準樹類型,有三個不一樣的樹:x,prs和vnd。你使用的標準樹須要取決於你開發的項目

  •  未註冊的樹(x)主要表示本地和私有環境
  •  私有樹(prs)主要表示沒有商業發佈的項目
  •  供應商樹(vnd)主要表示公開發布的項目

後面幾個參數依次爲應用名稱(通常爲應用域名)、版本號、指望的返回格式。

至於具體把版本號放在什麼地方,這個問題一直存在很大的爭議,但因爲咱們大多數時間都在使用Laravel開發,應該使用 dingo/api 來快速構建應用,它採用第二種方式來管理API版本,而且已集成了標準的HTTP Response。

Endpoints

端點就是指向特定資源或資源集合的URL。在端點的設計中,你必須遵照下列約定:

  •  URL 的命名必須所有小寫
  •  URL 中資源(resource)的命名必須是名詞,而且必須是複數形式
  •  必須優先使用Restful類型的 URL
  •  URL 中不能出現-,必須用下劃線_代替
  •  URL必須是易讀的
  •  URL必定不可暴露服務器架構

來看一個反例

再來看一個正列

HTTP 動詞

對於資源的具體操做類型,由HTTP動詞表示。經常使用的HTTP動詞有下面五個(括號裏是對應的SQL命令)。

  •  GET(SELECT):從服務器取出資源(一項或多項)。
  •  POST(CREATE):在服務器新建一個資源。
  •  PUT(UPDATE):在服務器更新資源(客戶端提供改變後的完整資源)。
  •  PATCH(UPDATE):在服務器更新資源(客戶端提供改變的屬性)。
  •  DELETE(DELETE):從服務器刪除資源。

其中

1 刪除資源必須用DELETE方法
2 建立新的資源必須使用POST方法
3 更新資源應該使用PUT方法
4 獲取資源信息必須使用GET方法

針對每個端點來講,下面列出全部可行的HTTP動詞和端點的組合

請求方法 URL 描述
GET /zoos 列出全部的動物園(ID和名稱,不要太詳細)
POST /zoos 新增一個新的動物園
GET /zoos/{zoo} 獲取指定動物園詳情
PUT /zoos/{zoo} 更新指定動物園(整個對象)
PATCH /zoos/{zoo} 更新動物園(部分對象)
DELETE /zoos/{zoo} 刪除指定動物園
GET /zoos/{zoo}/animals 檢索指定動物園下的動物列表(ID和名稱,不要太詳細)
GET /animals 列出全部動物(ID和名稱)。
POST /animals 新增新的動物
GET /animals/{animal} 獲取指定的動物詳情
PUT /animals/{animal} 更新指定的動物(整個對象)
PATCH /animals/{animal} 更新指定的動物(部分對象)
GET /animal_types 獲取全部動物類型(ID和名稱,不要太詳細)
GET /animal_types/{type} 獲取指定的動物類型詳情
GET /employees 檢索整個僱員列表
GET /employees/{employee} 檢索指定特定的員工
GET /zoos/{zoo}/employees 檢索在這個動物園工做的僱員的名單(身份證和姓名)
POST /employees 新增指定新員工
POST /zoos/{zoo}/employees 在特定的動物園僱傭一名員工
DELETE /zoos/{zoo}/employees/{employee} 從某個動物園解僱一名員工

Filtering

若是記錄數量不少,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。下面是一些常見的參數。

  •  ?limit=10:指定返回記錄的數量
  •  ?offset=10:指定返回記錄的開始位置。
  •  ?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。
  •  ?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序。
  •  ?animal_type_id=1:指定篩選條件

全部URL參數必須是全小寫,必須使用下劃線類型的參數形式。

常用的、複雜的查詢應該標籤化,下降維護成本。如

GET /trades?status=closed&sort=sortby=name&order=asc # 可爲其定製快捷方式 GET /trades/recently_closed 

Authentication

應該使用OAuth2.0的方式爲 API 調用者提供登陸認證。必須先經過登陸接口獲取Access Token後再經過該token調用須要身份認證的API。

Oauth 的端點設計示列

  •  RFC 6749 /token
  •  Twitter /oauth2/token
  •  Fackbook /oauth/access_token
  •  Google /o/oauth2/token
  •  Github /login/oauth/access_token
  •  Instagram /oauth/authorize

客戶端在得到access token的同時必須在響應中包含一個名爲expires_in的數據,它表示當前得到的token會在多少秒後失效。

{ "access_token": "token....", "token_type": "Bearer", "expires_in": 3600 }

客戶端在請求須要認證的API時,必須在請求頭Authorization中帶上access_token。

Authorization: Bearer token...

當超過指定的秒數後,access token就會過時,再次用過時/或無效的token訪問時,服務端應該返回invalid_token的錯誤或401錯誤碼。

HTTP/1.1 401 Unauthorized Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "error": "invalid_token" }

Laravel 開發中,應該使用 JWT 來爲管理你的 Token,而且必定不可在api中間件中開啓請求session。

Response

全部的API響應,必須遵照HTTP設計規範,必須選擇合適的HTTP狀態碼。必定不可全部接口都返回狀態碼爲200的HTTP響應,如:

HTTP/1.1 200 ok Content-Type: application/json Server: example.com { "code": 0, "msg": "success", "data": { "username": "username" } }

HTTP/1.1 200 ok Content-Type: application/json Server: example.com { "code": -1, "msg": "該活動不存在", }

下表列舉了常見的HTTP狀態碼

狀態碼 描述
1xx 表明請求已被接受,須要繼續處理
2xx 請求已成功,請求所但願的響應頭或數據體將隨此響應返回
3xx 重定向
4xx 客戶端緣由引發的錯誤
5xx 服務端緣由引發的錯誤

只有來自客戶端的請求被正確的處理後才能返回2xx的響應,因此當 API 返回2xx類型的狀態碼時,前端必須認定該請求已處理成功。

必須強調的是,全部API必定不可返回1xx類型的狀態碼。當API發生錯誤時,必須返回出錯時的詳細信息。目前常見返回錯誤信息的方法有兩種:

一、將錯誤詳細放入HTTP響應首部;

X-MYNAME-ERROR-CODE: 4001 X-MYNAME-ERROR-MESSAGE: Bad authentication token X-MYNAME-ERROR-INFO: http://docs.example.com/api/v1/authentication 

二、直接放入響應實體中;

HTTP/1.1 401 Unauthorized Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:02:59 GMT Connection: keep-alive {"error_code":40100,"message":"Unauthorized"}

考慮到易讀性和客戶端的易處理性,咱們必須把錯誤信息直接放到響應實體中,而且錯誤格式應該知足以下格式:

{ "message": "您查找的資源不存在", "error_code": 404001 }

其中錯誤碼(error_code)必須和HTTP狀態碼對應,也方便錯誤碼歸類,如:

HTTP/1.1 429 Too Many Requests Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:15:52 GMT Connection: keep-alive {"error_code":429001,"message":"你操做太頻繁了"}
HTTP/1.1 403 Forbidden Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:19:27 GMT Connection: keep-alive {"error_code":403002,"message":"用戶已禁用"}

應該在返回的錯誤信息中,同時包含面向開發者和麪向用戶的提示信息,前者可方便開發人員調試,後者可直接展現給終端用戶查看如:

{ "message": "直接展現給終端用戶的錯誤信息", "error_code": "業務錯誤碼", "error": "供開發者查看的錯誤信息", "debug": [ "錯誤堆棧,必須開啓 debug 才存在" ] }

下面詳細列舉了各類狀況 API 的返回說明。

200 ok

200狀態碼是最多見的HTTP狀態碼,在全部 成功 的GET請求中,必須返回此狀態碼。HTTP響應實體部分必須直接就是數據,不要作多餘的包裝。

錯誤示例:

HTTP/1.1 200 ok Content-Type: application/json Server: example.com { "user": { "id":1, "nickname":"fwest", "username": "example" } }

正確示例:

一、獲取單個資源詳情

{ "id": 1, "username": "godruoyi", "age": 88, }

二、獲取資源集合

[ { "id": 1, "username": "godruoyi", "age": 88, }, { "id": 2, "username": "foo", "age": 88, } ]

三、額外的媒體信息

{ "data": [ { "id": 1, "avatar": "https://lorempixel.com/640/480/?32556", "nickname": "fwest", "last_logined_time": "2018-05-29 04:56:43", "has_registed": true }, { "id": 2, "avatar": "https://lorempixel.com/640/480/?86144", "nickname": "zschowalter", "last_logined_time": "2018-06-16 15:18:34", "has_registed": true } ], "meta": { "pagination": { "total": 101, "count": 2, "per_page": 2, "current_page": 1, "total_pages": 51, "links": { "next": "http://api.example.com?page=2" } } } }

其中,分頁和其餘額外的媒體信息,必須放到meta字段中。

201 Created

當服務器建立數據成功時,應該返回此狀態碼。常見的應用場景是使用POST提交用戶信息,如:

  •  添加了新用戶
  •  上傳了圖片
  •  建立了新活動

等,均可以返回201狀態碼。須要注意的是,你能夠選擇在用戶建立成功後返回新用戶的數據

HTTP/1.1 201 Created Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:13:40 GMT Connection: keep-alive { "id": 1, "avatar": "https:\/\/lorempixel.com\/640\/480\/?32556", "nickname": "fwest", "last_logined_time": "2018-05-29 04:56:43", "created_at": "2018-06-16 17:55:55", "updated_at": "2018-06-16 17:55:55" }

也能夠返回一個響應實體爲空的HTTP Response如:

HTTP/1.1 201 Created Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:12:20 GMT Connection: keep-alive 

這裏咱們應該採用第二種方式,由於大多數狀況下,客戶端只須要知道該請求操做成功與否,並不須要返回新資源的信息。

202 Accepted

該狀態碼錶示服務器已經接受到了來自客戶端的請求,但還未開始處理。經常使用短信發送、郵件通知、模板消息推送等這類很耗時須要隊列支持的場景中;

返回該狀態碼時,響應實體必須爲空。

HTTP/1.1 202 Accepted Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:25:15 GMT Connection: keep-alive 

204 No Content

該狀態碼錶示響應實體不包含任何數據,其中:

  •  在使用DELETE方法刪除資源 成功 時,必須返回該狀態碼
  •  使用PUT、PATCH方法更新數據 成功 時,也應該返回此狀態碼
HTTP/1.1 204 No Content Server: nginx/1.11.9 Date: Sun, 24 Jun 2018 09:29:12 GMT Connection: keep-alive

3xx 重定向

全部API必定不可返回3xx類型的狀態碼。由於3xx類型的響應格式通常爲下列格式:

HTTP/1.1 302 Found Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 09:41:50 GMT Location: https://example.com Connection: keep-alive <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="refresh" content="0;url=https://example.com" /> <title>Redirecting to https://example.com</title> </head> <body> Redirecting to <a href="https://example.com">https://example.com</a>. </body> </html>

API必定不可返回純HTML結構的響應;若必定要使用重定向功能,應該返回一個響應實體爲空的3xx響應,並在響應頭中加上Location字段:

HTTP/1.1 302 Found Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:52:50 GMT Location: https://godruoyi.com Connection: keep-alive 

400 Bad Request

因爲明顯的客戶端錯誤(例如,請求語法格式錯誤、無效的請求、無效的簽名等),服務器應該放棄該請求。

當服務器沒法從其餘 4xx 類型的狀態碼中找出合適的來表示錯誤類型時,都必須返回該狀態碼。

HTTP/1.1 400 Bad Request Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 13:22:36 GMT Connection: keep-alive {"error_code":40000,"message":"無效的簽名"}

401 Unauthorized

該狀態碼錶示當前請求須要身份認證,如下狀況都必須返回該狀態碼。

  •  未認證用戶訪問須要認證的 API
  •  access_token 無效/過時

客戶端在收到401響應後,都應該提示用戶進行下一步的登陸操做。

HTTP/1.1 401 Unauthorized Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked WWW-Authenticate: JWTAuth Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 13:17:02 GMT Connection: keep-alive "message":"Token Signature could not be verified.","error_code": "40100"}

403 Forbidden

該狀態碼能夠簡單的理解爲沒有權限訪問該請求,服務器收到請求但拒絕提供服務。

如當普通用戶請求操做管理員用戶時,必須返回該狀態碼。

HTTP/1.1 403 Forbidden Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 13:05:34 GMT Connection: keep-alive {"error_code":40301,"message":"權限不足"}

404 Not Found

該狀態碼錶示用戶請求的資源不存在,如

  •  獲取不存在的用戶信息 (get /users/9999999)
  •  訪問不存在的端點

都必須返回該狀態碼,若該資源已永久不存在,則應該返回410響應。

405 Method Not Allowd

當客戶端使用的HTTP請求方法不被服務器容許時,必須返回該狀態碼。

如客戶端調用了POST方法來訪問只支持 GET 方法的 API

該響應必須返回一個Allow頭信息用以表示出當前資源可以接受的請求方法的列表。

HTTP/1.1 405 Method Not Allowed Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Allow: GET, HEAD Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 12:30:57 GMT Connection: keep-alive {"message":"405 Method Not Allowed","error_code": 40500}

406 Not Acceptable

API在不支持客戶端指定的數據格式時,應該返回此狀態碼。如支持JSON和XML輸出的API被指定返回YAML格式的數據時。

Http 協議通常經過請求首部的 Accept 來指定數據格式

408 Request Timeout

客戶端請求超時時必須返回該狀態碼,須要注意的時,該狀態碼錶示 客戶端請求超時,在涉及第三方API調用超時時,必定不可返回該狀態碼。

409 Gonfilct

該狀態碼錶示由於請求存在衝突沒法處理。如經過手機號碼提供註冊功能的API,當用戶提交的手機號已存在時,必須返回此狀態碼。

HTTP/1.1 409 Conflict Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 12:19:04 GMT Connection: keep-alive {"error_code":40900,"message":"手機號已存在"}

410 Gone

和404相似,該狀態碼也表示請求的資源不存在,只是410狀態碼進一步表示所請求的資源已不存在,而且將來也不會存在。在收到410狀態碼後,客戶端應該中止再次請求該資源。

413 Request Entity Too Large

該狀態碼錶示服務器拒絕處理當前請求,由於該請求提交的實體數據大小超過了服務器願意或者可以處理的範圍。

此種狀況下,服務器能夠關閉鏈接以避免客戶端繼續發送此請求。

若是這個情況是臨時的,服務器應該返回一個Retry-After的響應頭,以告知客戶端能夠在多少時間之後從新嘗試。

414 Request-URI Too Long

該狀態碼錶示請求的URI長度超過了服務器可以解釋的長度,所以服務器拒絕對該請求提供服務。

415 Unsupported Media Type

一般表示服務器不支持客戶端請求首部Content-Type指定的數據格式。如在只接受JSON格式的API中放入XML類型的數據並向服務器發送,都應該返回該狀態碼。

該狀態碼也可用於如:只容許上傳圖片格式的文件,可是客戶端提交媒體文件非法或不是圖片類型,這時應該返回該狀態碼:

HTTP/1.1 415 Unsupported Media Type Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 12:09:40 GMT Connection: keep-alive {"error_code":41500,"message":"不容許上傳的圖片格式"}

429 Too Many Request

該狀態碼錶示用戶請求次數超過容許範圍。如API設定爲60次/分鐘,當用戶在一分鐘內請求次數超過 60 次後,都應該返回該狀態碼。而且也應該在響應首部中加上下列頭部:

X-RateLimit-Limit: 10 請求速率(由應用設定,其單位通常爲小時/分鐘等,這裏是 10次/5分鐘) X-RateLimit-Remaining: 0 當前剩餘的請求數量 X-RateLimit-Reset: 1529839462 重置時間 Retry-After: 120 下一次訪問應該等待的時間(秒)

列子

HTTP/1.1 429 Too Many Requests Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked X-RateLimit-Limit: 10 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1529839462 Retry-After: 290 Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 11:19:32 GMT Connection: keep-alive {"message":"You have exceeded your rate limit.","error_code":42900}

必須爲全部的 API 設置 Rate Limit 支持。

500 Internal Server Error

該狀態碼必須在服務器出錯時拋出,對於全部的500錯誤,都應該提供完整的錯誤信息支持,也方便跟蹤調試。

503 Service Unavailable

該狀態碼錶示服務器暫時處理不可用狀態,當服務器須要維護或第三方API請求超時/不可達時,都應該返回該狀態碼,其中如果主動關閉 API 服務,應該在返回的響應首部加上Retry-After頭部,表示多少秒後能夠再次訪問。

HTTP/1.1 503 Service Unavailable Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:56:20 GMT Retry-After: 60 Connection: keep-alive {"error_code":50300,"message":"服務維護中"}

其餘HTTP狀態碼請參考 HTTP 狀態碼- 維基百科

http://www.gitout.cn/?p=2536

相關文章
相關標籤/搜索