Express 异步错误自动捕获方案(wrapRouterStack)
在 Express 中,异步路由中的 throw new Error() 不会自动进入全局错误处理器,这是因为 Express 默认只捕获同步错误。
例如:
js
router.get('/demo', async (req, res) => {
throw new Error("Boom")
})这段代码不会触发全局错误处理中间件!
为了解决这个问题,我们可以使用 wrapRouterStack 自动为所有路由 handler 添加错误捕获包装,使同步和异步的错误都能正确传递给 next(err)。
✨ wrapRouterStack — 自动包装所有路由的错误捕获
ts
/**
* 为指定 Router 实例中的所有路由处理函数添加异步错误捕获包装。
*
* Express 默认不会自动捕获 async/await 中的异常,
* 如果异步路由中 `throw new Error()`,应用不会自动进入全局错误处理中间件。
*
* 本函数通过遍历 Router 内部的 `stack`,
* 对每个 route 中的 handler 函数进行封装:
* - 对同步错误使用 try/catch 捕获;
* - 对返回 Promise 的异步函数添加 `.catch(next)`;
*
* 从而确保任何同步或异步异常都能被正确传递给全局错误处理器。
*
* @param {Router} router - 需要进行错误捕获包装的 Express Router 实例。
*
* @example
* const router = Router()
* router.get('/demo', async (req, res) => {
* throw new Error('This will be caught!')
* })
* wrapRouterStack(router)
*/
function wrapRouterStack(router: Router): void {
router.stack.forEach(layer => {
if (!layer.route) return
for (const stack of layer.route.stack) {
const handler = stack.handle
if (typeof handler === 'function') {
stack.handle = function (req: Request, res: Response, next: NextFunction) {
try {
const ret = handler(req, res, next)
// 如果返回 Promise,则捕获异步异常
if (ret && typeof ret.then === 'function') {
ret.catch(next)
}
} catch (err) {
// 捕获同步异常
next(err)
}
}
}
}
})
}✨ 全局错误处理中间件(与 wrapRouterStack 搭配)
ts
import type { Request, Response, NextFunction } from 'express'
module.exports = (error: any, req: Request, resp: Response, next: NextFunction) => {
// 如果响应头已经发出,交给 Express 默认处理
if (resp.headersSent) {
return next(error)
}
// 如果有自定义的失败处理函数,则调用
if (resp.fail) {
return resp.fail(error)
}
const status = error.status || error.statusCode || 500
resp.status(status).json({
success: false,
message: error.message || '服务器内部错误',
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
})
}✔ 使用方式示例
ts
const router = Router()
router.get('/test', async (req, res) => {
throw new Error("Test error")
})
wrapRouterStack(router)
app.use('/api', router)无论是:
- 同步错误
- 异步错误
- Promise.reject()
- throw new Error
都能正确进入全局错误处理中间件。
🎯 总结
| 问题 | 解决方式 |
|---|---|
| Express 默认无法捕获 async/await 中的错误 | 用 wrapRouterStack 自动为所有路由补充 .catch(next) |
| 无法捕获同步错误 | wrapRouterStack 内部的 try/catch |
| 需要统一的错误返回结构 | 全局错误处理中间件 |
这是 Express 最优雅、可扩展、零侵入的错误处理方案。