# 從 LINE Bot 實作理解 Webhook 機制
## 為什麼寫這篇
最近在做 LINE Bot,發現 Webhook 這個東西雖然概念簡單,但實際串接時還是踩了不少坑。
一開始照著文件設定,Bot 就是不回應。檢查 log 發現 LINE 有發請求過來,但訊息就是發不出去。後來才發現是 `replyToken` 過期了——因為我處理太慢,超過 5 秒才回 200,LINE 就放棄重試了。
還有一次是使用者反應「為什麼傳一次訊息,機器人回三次?」查了半天才知道是網路抖動造成 LINE 重送 Webhook,而我沒做冪等性處理 (同一個操作執行多次,結果都跟執行一次一樣)。
這些坑讓我重新思考 Webhook 的設計邏輯:為什麼要快速回應?為什麼要驗證簽名?為什麼要處理重複請求?這篇筆記記錄了實作過程中的一些心得。
---
## 如果不用 Webhook 會怎樣?
假設 LINE 不提供 Webhook,我們只能用輪詢 (Polling) 的方式:
```python
# 輪詢方式 - 持續詢問
while True:
response = requests.get('https://api.line.me/v2/messages')
if response.json()['has_new_message']:
messages = response.json()['messages']
for msg in messages:
process_message(msg)
time.sleep(1) # 每秒問一次
```
### 輪詢的問題
| 問題 | 說明 |
|------|------|
| **資源浪費** | 即使沒訊息也要一直發請求,99% 的請求都是白費的 |
| **延遲** | 輪詢間隔 1 秒,訊息最多延遲 1 秒才收到 |
| **API 限制** | 高頻率輪詢容易被 rate limit |
| **伺服器負擔** | 如果有 1000 個 Bot,LINE 每秒要處理 1000 次查詢 |
### Webhook 的優勢
```python
# Webhook - 事件驅動
@app.post('/webhook')
async def webhook(request: Request):
event = await request.json()
process_message(event)
return {'status': 'ok'}
```
| 優勢 | 說明 |
|------|------|
| **即時** | 訊息一到立刻通知,零延遲 |
| **省資源** | 只在有事件時才發請求 |
| **可擴展** | 不管多少 Bot,LINE 只在需要時才發通知 |
簡單說就是:**Pull = 我問你,Push = 你通知我**
---
## LINE Bot 的 Webhook 機制
### 架構圖
```
使用者 → LINE Platform → Webhook → 你的 Bot 後端 → LINE Messaging API
```
整個流程:
1. 使用者傳訊息到 LINE
2. LINE Platform 發送 POST 請求到你設定的 Webhook URL
3. 你的 Bot 處理訊息並回傳 200 OK
4. Bot 透過 LINE Messaging API 回覆訊息
---
## 實作細節與陷阱
### 1. 簽名驗證
LINE 會在 Header 加上 `X-Line-Signature`,用來驗證請求確實來自 LINE Platform。
```python
def validate_signature(body: bytes, signature: str) -> bool:
"""驗證 LINE Webhook 簽名"""
hash_value = hmac.new(
CHANNEL_SECRET.encode('utf-8'),
body,
hashlib.sha256
).digest()
return base64.b64encode(hash_value).decode('utf-8') == signature
```
為什麼需要驗證?
任何知道你 Webhook URL 的人都能發送偽造請求。不驗證簽名的話,攻擊者可以假冒 LINE 發送惡意資料。
### 2. 快速回覆與非同步處理
LINE 要求在 **5 秒內回覆 200 OK**,否則會判定 Webhook 失敗並重試。
```python
# ❌ 錯誤:同步處理可能超時
@app.post('/webhook')
async def callback(request: Request):
body = await request.body()
events = json.loads(body)['events']
for event in events:
process_complex_task(event) # 可能超過 5 秒
return 'OK' # 太慢了
# ✅ 正確:先回覆,再處理
@app.post('/webhook')
async def callback(request: Request):
body = await request.body()
# 立即回覆
asyncio.create_task(process_events(body))
return 'OK'
async def process_events(body: bytes):
events = json.loads(body)['events']
for event in events:
await process_complex_task(event)
```
或者使用訊息佇列:
```python
import redis
from rq import Queue
q = Queue(connection=redis.Redis())
@app.post('/webhook')
async def callback(request: Request):
body = await request.body()
# 丟到背景處理
q.enqueue(process_events, body)
return 'OK'
```
### 3. 冪等性處理
網路不穩定時,LINE 可能重送相同的 Webhook。必須確保重複處理不會產生副作用。
```python
import redis
redis_client = redis.Redis()
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
message_id = event.message.id
# 檢查是否已處理
if redis_client.exists(f'processed:{message_id}'):
return
# 標記為已處理 (TTL 24 小時)
redis_client.setex(f'processed:{message_id}', 86400, '1')
# 實際處理邏輯
process_message(event)
```
或使用 `replyToken` 的特性:每個 token 只能用一次,LINE 會自動處理重複。
### 4. Reply vs Push
LINE Bot 有兩種發訊息方式:
**Reply (使用 replyToken)**:
```python
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='回覆訊息')
)
```
- 只能在收到 Webhook 後立即使用
- 每個 replyToken 只能用一次
- 不計費
**Push (使用 userId)**:
```python
line_bot_api.push_message(
user_id,
TextSendMessage(text='主動推播')
)
```
- 可以隨時主動發送
- 需要 user_id
- 計費(免費額度外)
---
## 開發環境設定
### 本地開發:使用 ngrok
LINE Webhook 必須是公開的 HTTPS 網址,開發時可用 ngrok:
```bash
# 啟動 Bot
uvicorn main:app --reload --port 8000
# 另一個 terminal
ngrok http 8000
```
ngrok 是一個反向代理服務,可以將本地開發環境下的服務臨時公開到網際網路,提供一個公開的網址讓外部連線存取。
會提供類似 `https://abc123.ngrok.io` 的網址,填入 LINE Developers Console。
---
## 總結
Webhook 的核心就是**事件驅動的 HTTP 回調**:
1. 註冊公開的 HTTPS 端點
2. 對方在事件發生時發送 POST 請求
3. 快速回覆 200 OK
4. 非同步處理實際邏輯
理解這個模式後,不只是 LINE Bot,許多第三方服務整合(GitHub、Stripe、Discord)都是類似的概念。
重點在於:
- **安全性**:驗證請求來源
- **可靠性**:處理重試與冪等性
- **效能**:先回覆,再處理
---
## 參考資源
- https://medium.com/@justinlee_78563/line-bot-%E7%B3%BB%E5%88%97%E6%96%87-%E4%BB%80%E9%BA%BC%E6%98%AF-webhook-d0ab0bb192be
- https://ithelp.ithome.com.tw/m/articles/10193212
- [LINE Messaging API 文件](https://developers.line.biz/en/docs/messaging-api/)
- [ngrok](https://ngrok.com/)