在 Express 中,Middleware 就是「請求與回應之間的過濾層」。
它會攔截進來的請求,進行驗證、轉換或紀錄,然後決定是否繼續交給下一層處理。
Middleware 的函式格式是固定的:
(req, res, next) => { ... }
三個核心角色:
如果忘記呼叫 next()
,請求就會停在這一層,導致「卡死」。
假設「請求」就像是一顆球,而每一層 中間件 (Middleware) 就像是一段連接管道。
這顆球(請求)要到達最終的處理器(Route Handler),必須 依序通過每一段管道。
在每個管道(中間件)裡,系統會依照該中間件的邏輯進行判斷或處理,例如驗證、紀錄或轉換資料。
如果一切通過,就會被送往下一個管道。
如果某一層中間件直接回應了(例如 res.send())或阻擋了(沒有呼叫 next()),
那麼這顆球就不會再往下傳,請求也就停在這裡。
只有順利通過所有檢查,球才會抵達最終的處理器(Route Handler),完成整個請求-回應流程。
Client
│
▼
Middleware A (驗證登入)
│
▼
Middleware B (Body Parser)
│
▼
Middleware C (Logger)
│
▼
Route Handler (主要邏輯)
│
▼
Error Middleware (錯誤處理)
│
▼
Response 回給 Client
規則:
res.send()
或 res.json()
回應了,後面的中間件就不會再跑。用 app.use()
註冊,會套用在所有請求上。
app.use((req, res, next) => {
console.log(`[LOG] ${req.method} ${req.url}`);
next();
});
有時候只想在特定路由加上檢查或處理,就可以這樣寫:
function auth(req, res, next) {
const token = req.headers.authorization;
if (token === "Bearer token123") {
next(); // ✅ 驗證通過
} else {
res.status(401).json({ error: "Unauthorized" });
}
}
app.get("/dashboard", auth, (req, res) => {
res.send("歡迎來到管理後台 🎉");
});
👉 /dashboard
會經過驗證,但 /about
、/notes
不會。
也可以一次掛多個中間件:
function logger(req, res, next) {
console.log("⏰ Time:", Date.now());
next();
}
function checkAuth(req, res, next) {
if (req.headers.authorization) next();
else res.status(401).send("請先登入");
}
app.get("/profile", [logger, checkAuth], (req, res) => {
res.send("這是個人檔案頁面 👤");
});
Express 提供一些實用的內建中間件:
app.use(express.json()); // 處理 JSON body
app.use(express.urlencoded()); // 處理表單
app.use(express.static("public")); // 提供靜態檔案
安裝套件,擴充功能:
import morgan from "morgan";
import cors from "cors";
app.use(morgan("dev"));
app.use(cors());
Express 有專屬的「錯誤處理器」,格式必須是四個參數 (err, req, res, next)
:
app.use((err, req, res, next) => {
console.error("❌ 發生錯誤:", err.message);
res.status(500).json({ error: "伺服器錯誤" });
});
它的角色是「最後防線」,統一處理應用程式的例外,避免伺服器直接掛掉或使用者無限等待。
Express 不會自動捕捉 async/await 的錯誤。
如果你在路由裡忘了加 try/catch
,一旦出錯,請求就會卡住,永遠不會進到錯誤中間件。
app.get("/user", async (req, res) => {
const data = await db.findUser(); // 如果這裡出錯 → 卡死
res.json(data);
});
app.get("/user", async (req, res, next) => {
try {
const data = await db.findUser();
res.json(data);
} catch (err) {
next(err); // 交給錯誤處理中間件
}
});
function asyncHandler(fn) {
return (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
}
app.get("/user", asyncHandler(async (req, res) => {
const data = await db.findUser();
res.json(data);
}));
以往在開發過程中常會遇到一些狀況,
因此整理成「✅ 正確 vs ❌ 錯誤」示範:
✅ 正確:
app.use(express.json()); // 放在路由前
app.post("/api/echo", (req, res) => res.json({ received: req.body }));
❌ 錯誤:
app.post("/api/echo", (req, res) => res.json({ received: req.body })); // req.body 永遠 undefined
app.use(express.json()); // 放錯順序
✅ 正確:
app.use((req, res, next) => {
console.log(`[LOG] ${req.method} ${req.url}`);
next();
});
❌ 錯誤:
app.use((req, res, next) => {
console.log(`[LOG] ${req.method} ${req.url}`);
// 忘記 next() → 請求卡住
});
✅ 正確:
import rateLimit from "express-rate-limit";
const limiter = rateLimit({ windowMs: 60000, max: 5 }); //每分鐘 最多請求五次
app.use("/api/", limiter);
// 沒有限制 → API 可能被暴力攻擊
✅ 正確:
app.get("/user", asyncHandler(async (req, res) => {
const data = await db.findUser();
res.json(data);
}));
❌ 錯誤:
app.get("/user", async (req, res) => {
const data = await db.findUser(); // 出錯不會被捕捉
res.json(data);
});
在 Express 中,所有請求都必須經過一層層的 Middleware,就像關卡一樣,決定要不要放行、要不要轉換資料,最後才交給路由處理。
express.json()
、express.static()
,最常用也最基礎。morgan
、cors
、helmet
。next()
→ 請求會卡死。express.json()
要在使用 req.body
之前。asyncHandler
包裝,否則錯誤不會進錯誤處理層。