複習一下!並加入比較深入進階的部分,利用鐵人賽這個機會讓我再去認真查詢學習 RESTful API 的相關設計!雖然不是強制一定要這麼設計API,但是可以讓程式碼的可讀性更好!我想這是各位大大會想努力的地方。
確認一個資源
如之前的範例資源就是動物資源,設計RESTful API要先有一個客戶端資源,可以做查詢動物的資訊、建立、編輯、刪除,分別用HTTP不同的動詞,以客戶端(介接我們API的人)的方向去思考,不需要跟資料表一模一樣。
HTTP 動詞
- GET: 讀取資源
- POST: 新增資源
- PUT: 替換資源
- DELETE: 刪除資源
- PATCH: 更新資源部份內容
GET 的動作相較安全,它不會變動更改到伺服器的資訊,主要用來查詢資料。
POST、DELETE、PATCH、PUT 則會依照對應的動詞做需要的動作,寫入資料庫,或一些商業邏輯。
一定會是這種模式 動詞+資源
POST http://127.0.0.1/animal
PUT、PATCH 差別
PUT 通常做替換一個資源功能。
PATCH 修改資源的部分內容。
我自己是這麼定義的,假設資料表內已經有一筆資料內容如下
{
"id" : 1,
"name" : "小白" ,
"type" : "小型犬" ,
"birthday" : "2019/1/29"
}
PUT : 動物資料整個替換掉 PUT 方式請求網址 /animals/1 修改編號1的資料,回傳資料內容都是ID 1但裡面的內容整個不同。
PUT下面的內容,那麼ID 1的資料就會全部被修改成小黑的資訊,所以請求的時候必須填寫所有欄位不然資料會變成預設值。
{
"name":"小黑" ,
"type":"大型犬",
}
--結果--
{
"id" : 1,
"name" : "小黑" ,
"type" : "大型犬" ,
"birthday" : null
}
PATCH 一筆動物資料,我只想修改name 那就傳送含有欄位名稱的內容給API。
假設當時聽錯小白是大白那就只給需要修改的資料即可。
{
"name":"大白"
}
--結果--
{
"id" : 1,
"name" : "大白" ,
"type" : "小型犬" ,
"birthday" : "2019/1/29"
}
API路徑 前贅詞
GET http://127.0.0.1:3000/api/animal/1
再建立API的過程以及維護上,如果API程式改動可能會導致界接API的 client端會有錯誤,我目前是在網址加上前贅詞的方式,會建立類似這樣的開頭
例如:網域名稱 https://vnewin.com (是我的部落格)
API 會這樣加上前贅詞 https://vnewin.com/api/
或者 https://api.vnewin.com/
當作API的路徑
版本號的部分我偏向把它寫在網址上,所以會變成 https://vnewin.com/api/v1/
後面才開始接資源路徑。
版本號的另外一種寫法
可以把版本號寫在 Headers 裡面,下次我會試試看,但我應該會先把,大更新比如說 v1 寫在URI,小幅變動的版本號在寫在Headers。
URI 格式
建議已小寫為主兩個字之間用減號「-」隔開。
資源名詞
資源複數名詞
如果對於client端來說,animals 有很多可讀取的資源,建議加上複數,但我自己對於這方面還是覺得有點奇怪,像新建一筆動物資料,用 POST api/animals
我看起來是感覺怪怪的!
不過大多API設計一般都使用複數名詞。
唯一資源
但因為後來在設計User這樣的資源,對於一般的會員,就只會有一筆資料,他只能查詢他自己資料,刻意設計一個 users/1 來查詢自己的內容,對於客戶端的使用者覺得很沒有必要,而且與我管理端在操作 User API 有衝突,所以可能知道為什麼大多數用複數的原因。
假設一般會員,讀取他自己基本資料時,還要想辦法知道他的User id 實在有點奇怪。
就再有一天研究 GitHub 的 API 時發現 GET /user/subscriptions
,表示附上的token 登入會員的資源,對於user端來說,他可以這樣輕鬆查到自己的資料。
關聯資料
資源的層級架構,可以適當反應在URI上,例如 /animals/1/event
表示這隻動物的相關事件!
或是動物打狂犬疫苗,這個簡單的動作,因為狂犬病疫苗,每年要打一次,每次施打都做紀錄!因此可以請求
POST /animal/1/vaccination
這個請求會去新增施打狂犬疫苗資料表的資料。
{
"date" : "2019-10-01"
}
Resource | Utility API
目前我查詢到的認知最常分兩種,一種 資源型Resource 就是操作資源的CRUD,另一種 Utility 功能型檢查。
例如 檢查優惠券是否可以使用或是搜尋功能 API
GET /api/search?q={keywords}
可能還有一些特殊的操作,我會使用POST動詞來處理這些特殊的操作。
例如我們的系統想要讓使用者方便確認動物是否打過狂犬疫苗,也可以讓系統人性化一點訂製一個確認施打疫苗的 API 我認為也是可行的,最主要需求有符合系統的規劃,業主的希望。
所以再次強調,RESTful API就是一個設計的規範,沒有硬性規定一定要怎麼做,依照經驗打造一個好維護的API比較重要。
HTTP Header
最常看到的就是 Accept
以及 Authorization
我們之前的範例都有用到在發送請求的時候。
Accept
設定可以接受的回應內容類型Authorization
認證 Token 的資訊
以上這些都是定義URI 以及發送請求方面的建議,讓開發者有好的語意可以了解API的運行方式,可以多看多比較,找到一個最適合專案的運用方式。
Response 回傳建議
Response Body
回傳資料的格式有很多種,但現在 JSON 格式算主流,系統也普遍支援,JSON 比較簡潔與XML相比的話,所以越來越多人採用JSON作為資料的傳遞。採用JSON或XML或同時支援兩種格式,仍應視專案的實際需求而定。 可能客戶端的系統都很舊,都使用XML 那你就要想辦法支援!
Errors 定義
就像 Html 錯誤頁面向訪問者顯示有用的錯誤消息一樣,API應該以已知的錯誤提供有用的錯誤消息,像Google 的api 有些還有說明頁面得連結。
這邊要特別談到HTTP狀態。一定要返回對應的狀態碼,打致上可以分為三種,成功的狀態碼200系列、400系列使用者的問題、500系列伺服器出錯的問題。
一個好的API建議的可以規格化所有400系列的錯誤返回提示,在返回的錯誤JSON資料中,應該要替開發者提供一些東西。
- 錯誤代碼,方便查詢,也方便我們在開發API的過程撰寫文件
- 錯誤訊息
- 錯誤原因的敘述
{
"code" : 123456,
"message" : "發生錯誤!",
"description" : "有關錯誤的錯誤訊息"
}
PUT,PATCH、POST 請求的驗證錯誤附上欄位敘述,這邊我都是直接用 Laravel 內建的驗證表單功能,返回的錯誤格式!
HTTP狀態碼總覽
HTTP 定義了一堆可以從API返回的有意義的HTTP狀態碼。可以利用這些來幫助API客戶端判斷對應動作。
我整理以下比較常用的狀態碼!提供給各位參考
操作成功之狀態碼
200 OK
- 成功的GET,PUT,PATCH或DELETE請求以及不是用於創建的POST請求。201 Created
- 對POST的回應,創建成功。應與指向新資源位置的Location標頭結合使用204 No Content
- 成功請求不返回資料的回應(如DELETE請求)304 Not Modified
- 在HTTP緩存標頭設定時使用,If-Modified-Since
客戶端使用者的操作錯誤狀態碼
400 Bad Request
- 請求格式錯誤,資料無法解析401 Unauthorized
- 未提供或無效的身份驗證Token時403 Forbidden
- 身份驗證成功但無權訪問資源404 Not Found
- 請求不存在的資源時405 Method Not Allowed
- 當請求的HTTP方法不允許經過身份驗證的用戶時410 Gone
- 表示此端點的資源不再可用。有用作舊API版本的一攬子響應415 Unsupported Media Type
- 如果作為請求的一部分提供了錯誤的內容類型422 Unprocessable Entity
- 用於欄位資料驗證錯誤429 Too Many Requests
- 一定時間內請求次數超過伺服器設定值被拒絕
伺服器錯誤的錯誤狀態碼
500 Internal Server Error
- 伺服器端程式有誤,需要工程師修復
其它建議
- 雖然API應該是無狀態(stateless),但之前有批量刪除或是修改的需求,我是有使用先請求刪除資料給伺服器!回傳刪除編號,再請求刪除。
- Query parameter的部份 /api/search?q={keywords} 粗體部分,只要風格保持一致即可,REST 並沒有特殊規範。
- REST API 所呈現的資源就如我們之前所述 URI 上的名稱 animal,跟資料庫可以不一樣!不一定要相同。
結語
RESTful API是一種設計風格,這種風格使API設計具有整體一致性,今天整理了網路上多位大神的文章,吸收內化以後,並且結合自己以前開發的經驗,提供給大家參考。
易於維護、擴展,並且充份利用HTTP協定的特點。這篇文章列出一些我認為較重要的部份,希望能幫助不熟REST API的人,在最短時間對它有個初步了解。
參考資料
https://tw.twincl.com/programming/*641y
https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api