---
# System prepended metadata

title: 初級後端工程師養成之 —— 別再亂用 HTTP Method 啦！

---

初級後端工程師養成之 —— 別再亂用 HTTP Method 啦！
===

在 RESTful API 概念還沒有普及時，大部分的 API 寫法都只使用 GET 方法和 POST 方法，不改變系統狀態的就用 GET，改變系統狀態的就使用 POST，當然這已經是古早味的用法了，現在也不提倡這樣的做法，因為如果只使用這兩個方法會讓 API 的語意不清，還要特別看路由上寫了什麼才能知道 API 的實際功能。

不過現實情況就是，其實你自己的命名只有你自己看得懂，在多人協作開發時，不要把你腦袋裡面認為理所當然的事寫進程式又不解釋，如果有特殊的設計應該寫註解或者文件。

:::danger
本編文章重點！
1. 也許你知道 PATCH 和 PUT 大致有什麼不同，但你真的用的正確嗎？
2. GET 不安全？為什麼不安全？那直接不用就好了嗎？什麼情況應該使用，什麼情況必須違反 RESTful 原則不使用 GET 方法？
:::

簡單介紹 RESTful API
---

這個概念其實已經相當普及了，所以我簡單介紹一下就好，RESTful 只是一個方法論，只需要遵守下面幾個原則，就可以說自己的 API 是 RESTful API：

1. path 盡可能只包含名詞，如 ```/user/info```。
2. 每一個路由分割代表一種資源，例如```/user/info``` 和 ```/user/log``` 就可以代表使用者資訊和使用者操作紀錄等，也可以使用 ```/user``` 獲取全部 user。
3. API 的行為應該用 HTTP Method 來表達，而不是寫在路由中，例如你應該寫 
```PUT /user/info``` 而不是寫 ```POST /UpdateUserInfo```

:::info
就像 Go 語言中的 interface 一樣，所有條件都實現，你就是那個 interface ~
:::

雖然 RESTful 原則可以滿足大部分的場景，但也不一定要死守規則，也有可能會出現只靠名詞無法表達實際語意的情況，舉個最簡單的例子**用戶登入**，其實只要寫 ```POST /login``` 就可以了，如果硬要遵循 RESTful 原則，你可能會寫出 ```PATCH /user/token``` 之類的路由，反而模糊了語意。

HTTP Method
---

在實務上其實只會用到 GET、POST、PUT、PATCH、DELETE 方法，了解的人都了解，他們分別用在這些地方：

* **GET：** 從系統獲取資料時，對應到常說的 CRUD 中的 R（Read）
* **POST：** 向系統新增資料時，對應到常說的 CRUD 中的 C（Create）
* **PUT/PATCH：** 更新系統資料時，對應到常說的 CRUD 中的 U（Update）
* **DELETE：** 刪除系統資料時，對應到常說的 CRUD 中的 D（Delete）

有了這裡的概念之後我們就可以來探討本編文章的重點了。

GET 不安全的點？不要看到別人說不安全就盲目不使用
---

這裡要來解釋一下 GET 會被說不安全的點，了解哪裡不安全你才能在該用的時候用，不該用的時候不用。

這件事發生在我的資深同事身上，當初接手專案時發現整個專案大部分的 API 都使用 POST 方法，一個 GET 方法都沒有，我好奇問了我的資深同事為什麼這麼做，結果他只回應我簡單的一句「GET 不安全」。

這麼做也就導致如果不看文件根本不知道這個 API 是查詢資料還是新增資料，多了一層溝通成本，所以現在我們來探討一下，究竟什麼時候可以用，還有不安全在哪？

### GET 無法使用 Request Body，資料都是明文傳輸

所有的 HTTP Method 中，只有 GET 方法不能攜帶 Request Body，導致需要傳參數條件時必須使用 query 網址傳參，也就是傳遞過程中攜帶的參數都是明文的。

所以會被說不安全的點其實就只是因為**參數被明文傳輸**。

### 了解了問題點，其實大部分的查詢 API 還是可以使用 GET 方法

既然參數會被明文傳輸，那麼只要你傳遞的資訊都不要有敏感資訊就不會有安全性的問題，如果這個 API 需要權限驗證，你可以用 Authorization headers 傳遞 token 就行。

你可能會說，既然都是明文傳輸，那就有可能被人攔截然後修改傳入後端啊？這樣還是不安全。但請仔細想一下，之所以這個 API 考慮使用 GET 方法，就是因為這個 API 本質上不會做任何變更系統狀態的事，如果你的實際函式有執行變更系統狀態相關的操作，那麼你不應該使用 GET 方法。所以即使參數被人修改，也只是找不到資料，或者找到的資料不對而已，對系統沒有任何影響。

:::info
總結一下，GET 方法通常傳的參數會是查詢資料的過濾條件，過濾條件本身就不會是敏感資訊，所以如果只是查詢資料的 API，還是盡可能使用 GET 方法吧！
:::

PUT/PATCH 還是分不清楚該怎麼使用嗎？
---

如果你看過 HTTP Method 相關的討論文章你可能有看過這樣的說法：

:::warning
PUT 和 PATCH 都是更新使用的方法，差異是如果你使用 PUT 方法，系統的行為就會是沒有你要更新的那筆資料就會建立一筆資料，如果有資料就會更新對應的資料，也就是結合了 POST 和更新的方法，而 PATCH 方法則是這筆資料必定存在，你只是更新部分資料而已。
:::

但這麼說只對一半，因為當你實際下場做開發時，如果你想到更新要用什麼方法時腦袋的聲音是上面的觀念，那麼恭喜你幾乎所有的更新相關 API 你都會使用 PATCH 方法 🎉。（這件事發生在我的一個喜歡抱怨別人程式碼的新手工程師同事身上🤣，還是要多檢討自己啊）

### PUT/PATCH 真正的分別方法

確實兩者都是更新系統的資料，但 **PUT 是冪等的**，而 **PATCH 不是冪等的**。

冪等是什麼意思？就是用同樣的輸入，會得到一樣的結果。舉個例子，如果是更新使用者名稱，我把我的名字改成吳彥祖，那麼只要輸入一樣無論我更新幾次我都還是吳彥祖😎。

那麼 PATCH 用在什麼時候？最簡單的例子就是更新密碼，你需要輸入舊密碼以及新密碼，第一次你輸入舊密碼和你要改的新密碼，成功更新了，但第二次你再用同樣的輸入就會出錯，因為你的舊密碼已經變成你剛剛設定的新密碼了。這個情況就不是冪等的，因為你沒辦法用同樣的輸入達到同樣的執行結果。

然後是 PUT 及 PATCH 常見的分別方法，也就是部分更新和全部替換，這個原則還是在的，但請以**前端視角**來看部分更新和全部替換這件事。

例如你開一個 API，可以同時修改用戶的名稱以及頭像 URL，此時如果你的設計是使用 PUT，那麼我在輸入時只輸入用戶名稱，頭像 URL 留空了，那麼出來的結果就會是用戶名稱被更新，而頭像 URL 變成空；如果你的設計是使用 PATCH，那麼就只應該更新用戶名稱，而頭像 URL 應該保留原資料。

這裡為什麼我會說要以**前端視角**看這件事？因為用戶資料也許在資料庫中還存有其他欄位，例如帳號、密碼、暱稱等等，如果你以**後端視角**來看，那麼更新用戶名稱和頭像 URL 就只會是部分更新（因為只是資料庫中的其中兩個欄位），此時你心裡就會想用 PATCH，而前端不知道的情況下用戶沒更新的欄位就會傳空，然後就造成空資料覆蓋掉原資料了。

這樣一來什麼時候要用 PUT 什麼時候用 PATCH 就很清楚了，如果輸入欄位是可選的，而且後端邏輯會判斷資料是否為空，不為空就更新，此時的更新就應該使用 PATCH，如果所有欄位都是獨立的欄位，而且後端不會判斷資料是否為空，那麼就應該使用 PUT。

:::success
最後一點小小的心聲：很多基本觀念大家可能不重視，覺得能用就行，但如果開發時都是依照自己的想法想怎麼做就怎麼做，這樣會讓和你合作的工程師很困擾的，因為別人不會有和你一樣的想法，這也是為什麼我們需要一些共同的知識以此來減少溝通成本。
:::