Express 錯誤處理:構建強健的集中式例外監控系統
在開發過程中,錯誤(Errors)是不可避免的。與其在每個路由都寫重複的 try...catch 然後回傳錯誤,Express 推薦使用 「集中式錯誤處理中間件」 來統一控管應用的穩定性。
錯誤處理中間件 (Error Handling Middleware)
與普通中間件不同,錯誤處理中間件必須接收 四個參數:(err, req, res, next)。Express 透過參數數量來識別它是錯誤處理器。
// 核心語法
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
res.status(statusCode).json({
status: 'error',
message: err.message || '伺服器內部發生未知錯誤',
// 在開發環境才顯示 stack trace,防止資訊外洩
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
});
});
錯誤處理中間件必須掛載在所有路由與
express.static 的 最後面。如何觸發錯誤處理?
- 同步程式碼:直接
throw new Error(),Express 會自動捕捉並傳給處理器。 - 非同步程式碼 (Async/Await):必須手動呼叫
next(err)。
app.get('/api/data', async (req, res, next) => {
try {
const data = await database.find();
res.json(data);
} catch (err) {
// 呼叫 next 並傳入 err 物件, Express 就會跳過普通路由進入錯誤處理器
next(err);
}
});
進階:最佳實務 - 優雅的非同步包裝
為了避免在每個 async 路由都寫煩人的 try...catch,我們通常會寫一個包裝函式(Wrapper):
// catchAsync.js
const catchAsync = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch(next); // 自動將 Promise 錯誤傳給 next()
};
};
// 使用範例
app.get(
'/users',
catchAsync(async (req, res) => {
const users = await User.all();
res.json(users);
})
);
處理 404 (找不到路徑)
在 Express 中,404 並不是一個錯誤,而是「沒有匹配的路由」。我們在路由的最末尾、錯誤處理器之前,放一個捕捉器:
// 捕捉所有未定義的路徑
app.use('*', (req, res, next) => {
const err = new Error(`找不到路徑 ${req.originalUrl}`);
err.statusCode = 404;
next(err);
});
區分開發與正式環境
一個成熟的報錯系統應該根據 NODE_ENV 回傳不同的細節:
- 開發環境:回傳詳細的
stack追蹤資訊,方便工程師除錯。 - 正式環境:回傳友善且簡潔的提示,隱藏系統內部的路徑與邏輯細節。
總結
next(err)是非同步錯誤處理的鑰匙。- 建立一個 AppError 自定義類別能讓你更精準地控制錯誤狀態碼。
- 善用
catchAsync能讓你的 Route Handler 保持極致簡潔。