第 2 週 · API 設計與接上前端
讀取請求資料與輸入驗證
不相信客人填的字。先檢查,再收進留言清單。
正在打開這一關…
學習目標
- 從 POST 的 JSON 讀出欄位
- 為作者與內容寫下清楚的驗證規則
- 驗證失敗時回 400 與客人看得懂的句子
前端的表單檢查是為了體驗,客人仍可繞過它,直接呼叫你的 API。後端必須再檢查一次。這一課的原則是:凡是從請求進來的值,都先當成不可信。
用生活例子理解
點餐單寫「無限大份」或空白,廚房不該直接下鍋。你要先看:
- 有沒有把 JSON 送來,還是送來一堆不是 JSON 的字。
- 作者是不是短字串。
- 內容是不是有字,而且沒有長到失控。
生活比喻:驗證像門口的尺寸標示。行李箱太大就不要硬塞進置物櫃。你回的不是生氣的長文,是一句看懂的話,加上狀態碼
400。
規則先定死,程式才有依據:
author:去掉前後空白後 1 到 20 個字。body:去掉前後空白後 1 到 500 個字。- 多出來的欄位先忽略。不要因為客人多傳了
role: "admin"就相信。
動手做
大約 25 分鐘。
- 在昨天的 Worker 加上
POST /api/messages。 - 用案例裡的
readMessage。它回傳錯誤句子或整理好的物件,二選一。 - 啟動
npx wrangler dev。另開一個終端機,用curl試四種情況(Windows 可用 PowerShell 的curl.exe):
curl -i http://localhost:8787/api/messages \
-H 'content-type: application/json' \
-d '{"author":"Ada","body":"第一則"}'- 再試空白內容、超長作者、以及本文不是 JSON(
-d '你好')。 - 成功應是
201。失敗應是400,而且 JSON 裡有error。 - 看回應,確認沒有把整段例外堆疊回給呼叫者。
案例
const messages = [];
function readMessage(data) {
if (!data || typeof data !== "object") return { error: "請傳送 JSON 物件" };
const author = typeof data.author === "string" ? data.author.trim() : "";
const body = typeof data.body === "string" ? data.body.trim() : "";
if (author.length < 1 || author.length > 20) {
return { error: "作者請介於 1 到 20 個字" };
}
if (body.length < 1 || body.length > 500) {
return { error: "留言請介於 1 到 500 個字" };
}
return { author, body };
}
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname !== "/api/messages") {
return Response.json({ error: "找不到這個路徑" }, { status: 404 });
}
if (request.method === "GET") {
return Response.json(messages);
}
if (request.method === "POST") {
let data;
try {
data = await request.json();
} catch {
return Response.json({ error: "請傳送 JSON" }, { status: 400 });
}
const parsed = readMessage(data);
if (parsed.error) {
return Response.json({ error: parsed.error }, { status: 400 });
}
const message = {
id: messages.length + 1,
author: parsed.author,
body: parsed.body,
createdAt: new Date().toISOString(),
};
messages.push(message);
return Response.json(message, { status: 201 });
}
return Response.json({ error: "不支援這個方法" }, { status: 405 });
},
};messages 放在模組最外層,只是暫時的白板。Worker 可能同時有很多份程式在跑,白板也不會永遠留著。星期五會再強調一次,第 3 週換成資料庫。
常見錯誤
- 只在 HTML 的
required做檢查。用curl可以不經過你的網頁。 - 失敗仍回
200。前端的response.ok會是 true,錯誤處理就不會走。 - 把
error.message原樣回傳。有些例外會露出檔案路徑或資料表名字。 - 用
data.body.length之前沒確認它是字串。數字沒有你期望的trim。
自我檢查
- 為什麼前端檢查過了,後端還要再檢查?
- 本文不是 JSON 時,你回哪個狀態碼?
- 模組外層的陣列為什麼不能當成正式的儲存?