RESTful
是目前最流行的 API 規範,適用於 Web 接口規範的設計。讓接口易讀,且含義清晰。本文將介紹如何設計易於理解和使用的 API,而且藉助 Docker api 的實踐說明。docker
它的核心思想就是客戶端發出的數據操做指令都是「動詞 + 賓語」的結構,好比 GET /articles這個命令,GET是動詞,/articles是賓語。json
動詞一般來講就是五種 HTTP 方法,對應咱們業務接口的 CRUD 操做。而賓語就是咱們要操做的資源,能夠理解成面向資源設計。咱們所關注的數據就是資源。ubuntu
正確的例子api
有些客戶端只能使用GET和POST這兩種方法。服務器必須接受 POST 模擬其餘三個方法(PUT、PATCH、DELETE)。bash
這時,客戶端發出的 HTTP 請求,要加上 X-HTTP-Method-Override
屬性,告訴服務器應該使用哪個動詞,覆蓋 POST 方法。服務器
就是 API 的url ,是 HTTP 動詞做用的對象,因此應該是名詞。例如 /books 這個 URL 就是正確的,而下面的 URL 不是名詞,都是錯誤的寫法。restful
錯誤示範:app
GET /getAllUsers?name=jl
POST /createUser
POST /deleteUSer
複製代碼
URL 是名詞,那麼是使用複數仍是單數?ide
沒有統一的規定,可是咱們一般操做的數據多數是一個集合,好比 GET /books
,因此咱們就使用複數。網站
統一規範,建議都使用複數 URL, 好比 獲取 id = 2 的書 GET /books/2
要好於 GET /book/2
。
有時候咱們要操做的資源多是有多個層級,所以很容易寫多級 URL,好比獲取某個做者某種分類的文章。
GET /authors/2/
categories/2 獲取做者ID = 2 分類 = 2 的文章
這種 URL 不利於拓展,語義 也不清晰。
更好的方式就是 除了第一級,其餘級別都是經過查詢字符串表達。
正確方式:
GET /authors/12?categories=2
查詢已發佈的文章
錯誤 寫法: GET /artichels/published
正確寫法: GET /artichels?published=true
下面是一些常見的參數。
- ?limit=10:指定返回記錄的數量
- ?offset=10:指定返回記錄的開始位置。
- ?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。
- ?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序。
- ?animal_type_id=1:指定篩選條件
參數的設計容許存在冗餘,即容許API路徑和URL參數偶爾有重複。好比,GET /zoo/ID/animals 與 GET /animals?zoo-id=ID 的含義是相同的。推薦後者,避免出現多級URL。
客戶端的請求,服務求都必須響應,包含 HTTP 狀態碼和數據。
HTTP 狀態碼就是一個三位數,分紅五個類別。
200狀態碼錶示操做成功,可是不一樣的方法能夠返回更精確的狀態碼。
4xx狀態碼錶示客戶端錯誤,主要有下面幾種。
5xx狀態碼錶示服務端錯誤。通常來講,API 不會向用戶透露服務器的詳細信息,因此只要兩個狀態碼就夠了。
API 返回的數據格式,不該該是純文本,而應該是一個 JSON 對象,由於這樣才能返回標準的結構化數據。因此,服務器迴應的 HTTP 頭的 Content-Type 屬性要設爲 application/json 。
客戶端請求時,也要明確告訴服務器,能夠接受 JSON 格式,即請求的 HTTP 頭的ACCEPT 屬性也要設成 application/json。下面是一個例子。
有一種不恰當的作法是,即便發生錯誤,也返回200狀態碼,把錯誤信息放在數據體裏面,就像下面這樣。
錯誤例子:
HTTP/1.1 200 OK
ConteNTP-Type: application/json
{
"status": "fail",
"msg": "錯誤"
}
複製代碼
上面代碼中,解析數據體之後,才能得知操做失敗。
這張作法實際上取消了狀態碼,這是徹底不可取的。正確的作法是,狀態碼反映發生的錯誤,具體的錯誤信息放在數據體裏面返回。下面是一個例子。
正確方式:
HTTP/1.1 400 Bad Request
ConteNTP-Type: application/json
{
"status": "fail",
"msg": "錯誤"
}
複製代碼
接下來咱們分析 docker api 對於 restful 的使用,助於咱們在實際工做中合理設計。
docker 文檔 url :docs.docker.com/engine/api/…
GET /v1.19/containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1
經過 all=1&before=8dfafdbc3a40&size=1 過濾容器數據
GET /containers/(id or name)/json
GET /v1.19/containers/4fa6e0f0c678/json HTTP/1.1
GET /v1.19/containers/4fa6e0f0c678/top HTTP/1.1
假如想過濾進程等能夠經過查詢字符串實現
GET /v1.19/containers/4fa6e0f0c678/top?ps_args=aux HTTP/1.1
返回的數據
HTTP/1.1 200 OK
Content-Type: application/json
{
"Titles" : [
"USER","PID","%CPU","%MEM","VSZ","RSS","TTY","STAT","START","TIME","COMMAND"
]
"Processes" : [
[
"root","13642","0.0","0.1","18172","3184","pts/0","Ss","17:03","0:00","/bin/bash"
],
[
"root","13895","0.0","0.0","4348","692","pts/0","S+","17:15","0:00","sleep 10"
]
],
}
複製代碼
POST /containers/create
POST /v1.19/containers/create HTTP/1.1
Content-Type: application/json
Content-Length: 12345
{
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"FOO=bar",
"BAZ=quux"
],
"Cmd": [
"date"
],
"Entrypoint": null,
"Image": "ubuntu",
"Labels": {
"com.example.vendor": "Acme",
"com.example.license": "GPL",
"com.example.version": "1.0"
},
"Volumes": {
"/volumes/data": {}
}
}
複製代碼
根據容器 id 刪除一個容器,v是請求是否刪除 容器 volumes
DELETE /v1.19/containers/16253994b7c4?v=1 HTTP/1.1
Query parameters:
false
.false
.false
.