測驗:應用層協定與 API 設計 — 從 HTTP 到 WebSocket
共 5 題,點選答案後會立即顯示結果
1. HTTP 協定的通訊模式是什麼?
2. 在 MQTT 協定中,負責接收和轉發訊息的角色叫什麼?
3. 以下哪個 API 設計符合 RESTful 最佳實踐?
4. 你正在設計一個即時聊天 App,訊息需要即時送達,伺服器也要能主動推送訊息給客戶端。最適合用哪種協定來傳遞訊息?
5. 當你呼叫 API 收到 HTTP 狀態碼 429 時,代表什麼意思?應該怎麼做?
**系列**:系統設計白話文(第 3 篇,共 5 篇)
**難度**:L2-進階
**前置知識**:【系統設計白話文】#02 設計需求與網路基礎
**影片來源**:freeCodeCamp “[System Design Concepts Course and Interview Prep](https://www.youtube.com/watch?v=F2FmTdLtb_4)” by Hayk Simonyan
一句話說明
應用層協定決定了「前端跟後端怎麼對話」,API 設計決定了「對話的規則」。
你會學到什麼
讀完這篇,你會能夠:
- 比較 HTTP、WebSocket、WebRTC、MQTT 四種協定,知道它們各自在幹嘛
- 列出設計好用 API 的關鍵原則(版本控制、分頁、錯誤處理等)
- 碰到不同應用需求時,判斷該用哪種協定
先搞懂一件事:什麼是「應用層協定」?
上一篇我們講了網路基礎。你已經知道資料在網路上怎麼傳輸。但「傳輸」只是把資料搬過去,應用層協定決定的是——資料用什麼格式、按什麼規則交換。
打個比方:
- 傳輸層(TCP/UDP)=馬路和卡車,負責把東西運過去
- 應用層協定(HTTP 等)=包裹上的格式標籤,告訴收件人「這是什麼、要怎麼處理」
你每天用的 Web 應用,背後幾乎都在用應用層協定。這篇要介紹四種最常見的。
協定一:HTTP — 你最熟的那個
一句話說明
HTTP 是「一問一答」模式:客戶端發請求,伺服器回回應,然後結束。
最小範例
客戶端 → 伺服器:GET /api/users/123
伺服器 → 客戶端:{ "id": 123, "name": "Alice" }
(對話結束)
Code language: JavaScript (javascript)白話翻譯
HTTP 就像寄信:你寫一封信(請求)寄出去,對方收到後回你一封信(回應),就這樣。每次要新資料,都得再寄一封新的信。
核心概念翻譯
| 你會看到 | 意思 |
|---|---|
GET /users |
「請給我使用者列表」 |
POST /users |
「我要新增一個使用者」 |
PUT /users/123 |
「我要整個覆蓋使用者 123 的資料」 |
PATCH /users/123 |
「我要修改使用者 123 的某些欄位」 |
DELETE /users/123 |
「請刪掉使用者 123」 |
200 OK |
「好的,處理完了,這是結果」 |
404 Not Found |
「找不到你要的東西」 |
500 Internal Server Error |
「伺服器自己出問題了」 |
適合什麼情境?
- 大多數的 Web 應用(網頁瀏覽、表單送出、API 呼叫)
- 資料不需要「即時推送」的場景
- RESTful API 的基礎
看到 HTTP 請求時要確認
- [ ] 用了哪個動詞(GET / POST / PUT / DELETE)?
- [ ] 回應的狀態碼是什麼(2xx 成功、4xx 客戶端錯誤、5xx 伺服器錯誤)?
- [ ] 有沒有帶 Authorization header(認證資訊)?
協定二:WebSocket — 持續對話的通道
一句話說明
WebSocket 是「開一條電話線,雙方可以隨時說話」,不用每次都重新打電話。
最小範例
// 客戶端建立 WebSocket 連線
const ws = new WebSocket("ws://example.com/chat");
ws.onopen = () => { // 連線建立成功時
ws.send("Hello!"); // 傳訊息給伺服器
};
ws.onmessage = (event) => { // 收到伺服器訊息時
console.log(event.data); // 印出來
};
Code language: JavaScript (javascript)逐行翻譯
const ws = new WebSocket("ws://example.com/chat");
// ↑ 跟 example.com 建立一條持續連線(注意是 ws:// 不是 http://)
ws.onopen = () => {
// ↑ 「連線成功時」要做的事
ws.send("Hello!");
// ↑ 透過這條連線傳訊息出去
ws.onmessage = (event) => {
// ↑ 「收到訊息時」要做的事(伺服器可以隨時主動推訊息過來!)
console.log(event.data);
// ↑ 把收到的訊息印出來
Code language: JavaScript (javascript)HTTP vs WebSocket 對比
HTTP(一問一答):
客戶端 → 有新訊息嗎? → 伺服器:沒有
客戶端 → 有新訊息嗎? → 伺服器:沒有
客戶端 → 有新訊息嗎? → 伺服器:有!給你
(每次都要重新問)
WebSocket(持續連線):
客戶端 ←→ 伺服器(連線建立)
伺服器 → 有新訊息了!直接推給你
伺服器 → 又有一則!直接推給你
(連線一直開著,伺服器主動推送)
適合什麼情境?
- 聊天室(LINE、Discord 的即時訊息)
- 即時通知(股票報價、運動比分)
- 協作編輯(Google Docs 多人同時編輯)
- 線上遊戲(需要即時同步遊戲狀態)
看到 WebSocket 時要確認
- [ ] 連線有沒有處理斷線重連的邏輯?
- [ ] 有沒有處理
onerror和onclose事件? - [ ] URL 是
ws://還是wss://(加密版本)?
協定三:WebRTC — 不經過伺服器的點對點通訊
一句話說明
WebRTC 讓兩個瀏覽器直接對話,不用繞過伺服器,適合視訊通話。
運作方式白話版
一般通訊(經過伺服器):
Alice → 伺服器 → Bob
WebRTC(點對點):
Alice ←→ Bob(直接連線,不經過伺服器)
但要先「牽線」:
Alice → 信令伺服器(我想跟 Bob 通話)
Bob ← 信令伺服器(Alice 想跟你通話)
Alice ←→ Bob(好,我們直接連)
核心概念翻譯
| 你會看到 | 意思 |
|---|---|
| Peer-to-Peer (P2P) | 兩個裝置直接通訊,不經過中間人 |
| Signaling Server | 「牽線伺服器」,只負責讓雙方知道彼此存在 |
| STUN Server | 幫你找到自己的公開 IP(因為你在路由器後面) |
| TURN Server | 當兩方真的無法直連時,退而求其次當中繼站 |
| ICE Candidate | 可能的連線路徑,系統會自動選最快的那條 |
適合什麼情境?
- 視訊通話(Google Meet、Zoom 的部分功能)
- 語音通話
- 螢幕分享
- P2P 檔案傳輸
看到 WebRTC 時要確認
- [ ] 有沒有設定 STUN/TURN 伺服器?(沒有的話很多網路環境會連不上)
- [ ] 是純 P2P 還是有 fallback 到伺服器轉發?
- [ ] 有沒有處理權限請求(攝影機、麥克風)?
協定四:MQTT — IoT 設備的輕量級通訊
一句話說明
MQTT 是「訂報紙」模式:設備訂閱特定主題,有新資料時自動收到,專為低功耗設備設計。
運作方式白話版
發布-訂閱模式:
溫度感測器 → 發布到「kitchen/temperature」主題 → Broker(中間人)
↓
手機 App ← 訂閱「kitchen/temperature」主題 ← Broker
儀表板 ← 訂閱「kitchen/temperature」主題 ← Broker
感測器只管發布,不用知道誰在聽
手機和儀表板只管訂閱,不用知道誰在發
Broker 負責轉發
核心概念翻譯
| 你會看到 | 意思 |
|---|---|
| Broker | 訊息中間人,負責接收和轉發 |
| Publish | 發布訊息到某個主題 |
| Subscribe | 訂閱某個主題,有新訊息就收到 |
| Topic | 訊息的分類,像資料夾路徑(例如 home/bedroom/light) |
| QoS 0 | 最多送一次(可能漏掉) |
| QoS 1 | 至少送一次(可能重複) |
| QoS 2 | 剛好送一次(最可靠但最慢) |
適合什麼情境?
- IoT 設備(智慧家居感測器、工廠設備監控)
- 低頻寬、不穩定的網路環境
- 設備功耗很低,不能一直維持複雜連線
MQTT vs HTTP 差在哪?
HTTP(每次都完整請求):
感測器 → POST /api/temperature { "value": 25.3 }
↑ 每次要帶完整的 HTTP headers,很重
MQTT(輕量發布):
感測器 → 發布 "25.3" 到 "kitchen/temp"
↑ 封包很小,省電省頻寬
Code language: JavaScript (javascript)四種協定怎麼選?決策樹
遇到系統設計問題時,用這個流程判斷:
你的應用需要什麼?
│
├─ 一般的 Web API(讀寫資料)
│ → 用 HTTP(REST API)
│
├─ 即時雙向通訊(聊天、通知、即時更新)
│ → 用 WebSocket
│
├─ 視訊/語音通話、螢幕分享(需要低延遲 P2P)
│ → 用 WebRTC
│
└─ IoT 設備、感測器(低功耗、低頻寬)
→ 用 MQTT
一張表看懂四種協定
| 特性 | HTTP | WebSocket | WebRTC | MQTT |
|---|---|---|---|---|
| 通訊方向 | 一問一答 | 雙向 | 雙向(P2P) | 發布-訂閱 |
| 連線方式 | 每次新連線 | 持續連線 | 點對點 | 透過 Broker |
| 延遲 | 中 | 低 | 極低 | 低 |
| 適合場景 | Web API | 聊天/通知 | 視訊通話 | IoT |
| 複雜度 | 低 | 中 | 高 | 中 |
| 需要伺服器? | 是 | 是 | 只需信令伺服器 | 需要 Broker |
API 設計最佳實踐:讓你的 API 好用又好維護
知道了協定之後,最常用的還是 HTTP。而基於 HTTP 的 API 怎麼設計才好用?這就是 REST API 設計的核心問題。
好的 API vs 壞的 API
先看對比,你就知道差在哪:
壞的 API(看不出在幹嘛):
GET /getUser?id=123
POST /createNewUser
POST /deleteUser
GET /fetchAllUsersList
好的 API(一看就懂):
GET /v1/users/123 ← 取得使用者 123
POST /v1/users ← 新增使用者
DELETE /v1/users/123 ← 刪除使用者 123
GET /v1/users ← 取得使用者列表
原則一:用名詞不用動詞
壞:GET /getUsers ← 動詞 + 名詞,冗餘
好:GET /users ← HTTP 動詞已經表示動作了
翻譯:HTTP 方法(GET、POST、DELETE)本身就是動詞了,URL 只放名詞就好。
原則二:版本控制
/v1/users ← 第一版 API
/v2/users ← 第二版,可能改了回傳格式
翻譯:API 會改版。加版號讓舊的 App 不會因為你更新 API 而壞掉。
原則三:分頁(Pagination)
壞:GET /users ← 一次回傳全部 10 萬筆?伺服器炸了
好:GET /users?page=1&limit=20 ← 每次只回 20 筆,第 1 頁
翻譯:資料很多時,分批給。跟 Google 搜尋結果分頁一樣的道理。
常見的分頁參數
| 參數 | 意思 |
|---|---|
page=2&limit=20 |
第 2 頁,每頁 20 筆 |
offset=40&limit=20 |
跳過前 40 筆,取 20 筆 |
cursor=abc123 |
從上次結果的標記繼續往下取(效能最好) |
原則四:正確使用狀態碼
壞:永遠回 200,把錯誤藏在 body 裡
{
"status": 200,
"error": "User not found" ← 明明找不到,還回 200?
}
好:用正確的 HTTP 狀態碼
404 Not Found
{
"error": "User not found",
"code": "USER_NOT_FOUND"
}
Code language: JavaScript (javascript)必看懂的狀態碼分類
| 範圍 | 意思 | 常見 |
|---|---|---|
| 2xx | 成功 | 200 OK、201 Created |
| 3xx | 重新導向 | 301 永久搬家、302 暫時搬家 |
| 4xx | 你(客戶端)的問題 | 400 Bad Request、401 沒登入、403 沒權限、404 找不到、429 Too Many Requests |
| 5xx | 我(伺服器)的問題 | 500 Internal Error、503 Service Unavailable |
原則五:限流(Rate Limiting)
當你呼叫 API 太頻繁:
HTTP/1.1 429 Too Many Requests
{
"error": "Rate limit exceeded",
"retry_after": 60 ← 60 秒後再試
}
Code language: JavaScript (javascript)翻譯:API 有使用額度,超過就會被暫時擋住。保護伺服器不被打爆。
常見的限流回應 Header
X-RateLimit-Limit: 100 ← 每小時最多 100 次
X-RateLimit-Remaining: 23 ← 還剩 23 次
X-RateLimit-Reset: 1672531200 ← 額度重置的時間(Unix timestamp)
Code language: HTTP (http)原則六:一致的錯誤回應格式
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email format is invalid",
"details": [
{
"field": "email",
"issue": "Must be a valid email address"
}
]
}
}
Code language: JSON / JSON with Comments (json)翻譯:每個錯誤回應都長得一樣,前端才好統一處理。
實戰範例:從需求判斷該用什麼
範例 1:電商網站
需求:瀏覽商品、下單、查訂單
分析:
- 一般的讀寫操作 → HTTP REST API
- 訂單狀態更新通知 → 可以用 HTTP 輪詢,或升級用 WebSocket
選擇:以 HTTP 為主,通知部分視規模決定
範例 2:即時聊天 App
需求:一對一聊天、群組聊天、已讀通知
分析:
- 訊息要即時送達 → 需要持續連線
- 伺服器要能主動推訊息 → HTTP 做不到
選擇:WebSocket(訊息傳遞)+ HTTP(使用者管理、歷史訊息查詢)
範例 3:視訊會議
需求:多人視訊通話、螢幕分享
分析:
- 影音串流需要極低延遲 → WebSocket 延遲還是太高
- 最好不經過伺服器 → 點對點直連
選擇:WebRTC(影音串流)+ WebSocket(信令 + 聊天)+ HTTP(使用者管理)
範例 4:智慧工廠監控
需求:1000 個感測器回報溫度、濕度
分析:
- 設備功耗低 → 需要輕量級協定
- 資料是「發布」性質 → 不需要一問一答
- 多個面板需要訂閱同一批資料
選擇:MQTT(感測器資料收集)+ HTTP(管理介面)+ WebSocket(即時儀表板)
Vibe Coder 檢查點
看到系統架構或 API 設計時,確認這些:
協定選擇
- [ ] 這個功能需要即時雙向通訊嗎?(是 → 考慮 WebSocket)
- [ ] 需要視訊/語音嗎?(是 → 考慮 WebRTC)
- [ ] 是 IoT 設備嗎?(是 → 考慮 MQTT)
- [ ] 以上都不是?(→ HTTP 就夠了)
API 設計
- [ ] URL 有版本號嗎?(
/v1/users) - [ ] 有分頁嗎?(列表 API 一定要有)
- [ ] 狀態碼正確嗎?(不是都回 200)
- [ ] 有限流機制嗎?(公開 API 一定要有)
- [ ] 錯誤回應格式一致嗎?
必看懂 vs 知道就好
必看懂(會一直出現):
- HTTP 方法(GET、POST、PUT、DELETE)
- 狀態碼的分類(2xx、4xx、5xx)
- WebSocket 的 ws:// 連線和事件處理
- REST API 的 URL 命名規則
- 分頁和限流的基本概念
知道就好(遇到再查):
- gRPC:Google 的高效能 RPC 框架,用 Protocol Buffers 序列化
- GraphQL:Facebook 的查詢語言,客戶端指定要哪些欄位
- Server-Sent Events (SSE):伺服器單向推送,比 WebSocket 簡單
- HTTP/2 Server Push:伺服器主動推送資源
- AMQP:進階訊息佇列協定,比 MQTT 更複雜但功能更完整
Code language: JavaScript (javascript)本篇重點回顧
- HTTP:一問一答,最基本也最常用。REST API 就是建在 HTTP 上。
- WebSocket:持續連線、雙向通訊。適合聊天室、即時通知。
- WebRTC:點對點直連,不經過伺服器。適合視訊通話。
- MQTT:輕量級發布-訂閱。適合 IoT 設備。
- API 設計:好的 API 用名詞 URL、有版本號、有分頁、用正確狀態碼、有限流。
**下一篇預告**:我們會進入資料庫的世界,看看 SQL vs NoSQL 怎麼選、什麼時候該加快取。
進階測驗:應用層協定與 API 設計
共 5 題,包含情境題與錯誤診斷題。