前情提要
Node.js + Express 是開發 REST API 時最常見的組合。
這篇文章會試著寫出三大常見錯誤類型的處理方式:
- 同步錯誤(throw)
- 非同步錯誤(async/await)
- Stream error(例如檔案下載)
如果你用的是 Express 5,其實已經支援原生 async function 錯誤攔截,連 async wrapper 都可以省下!
Express 的錯誤處理 middleware 是怎麼運作的?
只要你呼叫 next(err),Express 就會跳到錯誤處理 middleware:
1 | app.use((err, req, res, next) => { |
這個 middleware 需要有四個參數,Express 會自動辨識它是錯誤處理 middleware。
同步錯誤怎麼處理?
這種最簡單,直接 throw 就可以:
1 | app.get('/sync-error', (req, res) => { |
Express 會自動幫你捕捉,送進錯誤 middleware,不需要特別處理。
Express 4 async/await 的錯誤要小心
Express 4 的情況:
你可能會寫這樣的 code:
1 | app.get('/async-error', async (req, res) => { |
這樣寫會直接讓整個程式 crash,因為 Express 4 不會自動處理 async function 裡的錯誤。
解法:自己包一層 asyncWrapper
1 | export const asyncWrapper = (fn) => (req, res, next) => { |
使用方式:
1 | app.get('/async-error', asyncWrapper(async (req, res) => { |
這樣就能安全把錯誤丟給錯誤 middleware 處理。
Express 5:原生支援 async function
如果你用的是 Express 5,那更簡單了,直接寫 async function,Express 就會自動捕捉錯誤,完全不需要 async wrapper!
1 | app.get('/async-error', async (req, res) => { |
是不是清爽多了?
!!注意:要確認你的專案使用的是 [email protected] 版本。
可以用以下指令確認
1 | npm list express |
Stream error:Express 捕不到
問題在哪?
stream 錯誤是透過 EventEmitter 的 ‘error’ 事件傳遞,Express 根本不知道有這回事,例如:
1 | app.get('/file', asyncWrapper(async (req, res) => { |
正確作法:自己監聽 ‘error’,再丟給 next()**
1 | app.get('/file', asyncWrapper(async (req, res, next) => { |
Bonus:包成一個工具函式
1 | // utils/streamErrorHandler.ts |
使用:
1 | app.get('/file', asyncWrapper(async (req, res, next) => { |
整體範例程式
1 | import express from 'express' |
如果你用的是 Express 5,可以把 asyncWrapper 都拿掉,程式碼會更簡潔!
小結
| 類型 | 會自動處理? | 解法 |
|---|---|---|
| 同步錯誤 | ✅ Express 自動處理 | 直接 throw 就好 |
| async/await 錯誤 | ❌ Express 4 不會處理 | 用 asyncWrapper(Express 4) |
| ✅ Express 5 自動處理 | 或直接寫 async function(Express 5) | |
| Stream error | ❌ 完全不會處理 | 監聽 stream.on(‘error’, next) |
參考
(fin)
