Express无法处理控制器或其他类中抛出的错误
Express异步控制器抛错无法触发错误中间件的解决方案
在Express中,路由处理函数同步抛出错误时能正常触发错误中间件,但控制器中的异步方法抛错时,程序直接崩溃,错误中间件不生效。
问题复现
同步路由抛错可正常触发错误中间件:
# routes.ts // 同步抛错,能触发错误中间件 router.get('/', (req, res) => { throw new BadRequestError("teste") })
但异步控制器方法抛错时失效:
# UserController.ts export class UserController { static async registerNewUser(req: Request, res: Response) { const newUser = new User(req.body) const alreadyExists = await User.findUser(newUser) if (alreadyExists) { throw new BadRequestError("teste") } } } # routes.ts router.post('/register', UserController.registerNewUser)
当前项目配置:
# app.ts const app = express() app.use(express.json()); app.use(router) app.use(errorMiddleware) export { app } # server.ts import { app } from "./app"; const PORT = process.env.PORT app.listen(PORT, () => console.log(`Server is running on port ${PORT}`))
原因
Express 默认只能捕获同步代码中抛出的错误。异步函数(含async/await)的错误会进入Promise的拒绝状态,无法被Express自动捕获,导致程序崩溃而不触发错误中间件。
解决方案
方案1:手动捕获错误并传递给next
在异步控制器方法中添加try/catch,捕获错误后调用next(err)将错误传递给错误中间件(需给方法添加NextFunction参数):
# UserController.ts import { Request, Response, NextFunction } from 'express'; import { BadRequestError } from '../errors/BadRequestError'; export class UserController { static async registerNewUser(req: Request, res: Response, next: NextFunction) { try { const newUser = new User(req.body) const alreadyExists = await User.findUser(newUser) if (alreadyExists) { throw new BadRequestError("teste") } // 其他业务逻辑:保存用户、返回响应 res.status(201).json({ message: 'User created' }) } catch (err) { next(err); // 将错误传递给错误中间件 } } }
方案2:使用通用异步包装器(推荐)
编写一个通用包装函数,自动捕获异步路由处理函数的错误并传递给next,避免重复编写try/catch:
# utils/asyncHandler.ts import { Request, Response, NextFunction } from 'express'; // 包装异步函数,自动捕获错误并传给next export const asyncHandler = (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) => { return (req: Request, res: Response, next: NextFunction) => { fn(req, res, next).catch(next); }; };
在路由中使用该包装器:
# routes.ts import { asyncHandler } from './utils/asyncHandler'; import { UserController } from './controllers/UserController'; router.post('/register', asyncHandler(UserController.registerNewUser));
方案3:确保错误中间件格式正确
Express的错误中间件必须包含4个参数(err, req, res, next),否则会被当作普通中间件处理:
# middleware/errorMiddleware.ts import { Request, Response, NextFunction } from 'express'; export const errorMiddleware = (err: any, req: Request, res: Response, next: NextFunction) => { // 打印错误栈用于调试 console.error('Error:', err.stack); // 自定义错误类需包含statusCode,否则默认返回500状态码 const statusCode = err.statusCode || 500; res.status(statusCode).json({ message: err.message || 'Internal Server Error', // 开发环境可返回错误栈,生产环境建议关闭 // stack: process.env.NODE_ENV === 'development' ? err.stack : undefined }); };
同时需确保自定义错误类正确设置原型链(避免instanceof判断失效):
# errors/BadRequestError.ts export class BadRequestError extends Error { statusCode = 400; constructor(message: string) { super(message); // 修复原型链,确保类型判断正常 Object.setPrototypeOf(this, BadRequestError.prototype); } }
内容的提问来源于stack exchange,提问作者SiqMello
相关产品推荐
相关产品推荐

