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最後面

如何觸發錯誤處理?

  1. 同步程式碼:直接 throw new Error(),Express 會自動捕捉並傳給處理器。
  2. 非同步程式碼 (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 追蹤資訊,方便工程師除錯。
  • 正式環境:回傳友善且簡潔的提示,隱藏系統內部的路徑與邏輯細節。

總結

  1. next(err) 是非同步錯誤處理的鑰匙。
  2. 建立一個 AppError 自定義類別能讓你更精準地控制錯誤狀態碼。
  3. 善用 catchAsync 能讓你的 Route Handler 保持極致簡潔。