---
# System prepended metadata

title: RESTful API
tags: [knowledge, api, http]

---

# RESTful API
###### tags: `api` `http`

## HTTP 動詞

- GET: 讀取資源
- POST: 新增資源
- PUT: 替換資源
- DELETE: 刪除資源
- PATCH: 更新資源部份內容

GET 的動作相較安全，它不會變動更改到伺服器的資訊，主要用來查詢資料。

POST、DELETE、PATCH、PUT 則會依照對應的動詞做需要的動作，寫入資料庫，或一些商業邏輯。



## 一定會是這種模式 動詞+資源

```
POST http://127.0.0.1/animal
```

## PUT、PATCH 差別

PUT 通常做**替換**一個資源功能。

PATCH **修改**資源的**部分**內容。

## API路徑 前贅詞

```
GET  http://127.0.0.1:3000/api/animal/1
```

再建立API的過程以及維護上，如果API程式改動可能會導致界接API的 client端會有錯誤，我目前是在網址加上前贅詞的方式，會建立類似這樣的開頭

例如：網域名稱 [https://vnewin.com](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
```

這個請求會去新增施打狂犬疫苗資料表的資料。

```json
{
  "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的過程撰寫文件
- 錯誤訊息
- 錯誤原因的敘述

```json
{
  "code" : 123456,
  "message" : "發生錯誤！",
  "description" : "有關錯誤的錯誤訊息"
}
```

PUT，PATCH、POST 請求的驗證錯誤附上欄位敘述，這邊我都是直接用 Laravel 內建的驗證表單功能，返回的錯誤格式！

## HTTP狀態碼總覽

HTTP 定義了一堆可以從API返回的有意義的HTTP狀態碼。可以利用這些來幫助API客戶端判斷對應動作。

我整理以下比較常用的狀態碼！提供給各位參考

### 操作成功之狀態碼

- `200 OK` - 成功的GET，PUT，PATCH或DELETE請求以及不是用於創建的POST請求。
- `201 Created` - 對POST的回應，創建成功，並可回傳新建資源物件的資料。
- `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的人，在最短時間對它有個初步了解。

## 參考資料

[更多HTTP 狀態碼](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status)

https://tw.twincl.com/programming/*641y

https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

https://charder.readbook.tw/api-design/restful/

[更多HTTP協定](https://zh.wikipedia.org/zh-tw/超文本传输协议)

jsend標準：https://github.com/omniti-labs/jsend

規劃RESTful API要注意什麼：https://noob.tw/restful-api/