如何解决Express错误处理中间件未被调用的问题
Express错误处理中间件无法捕获异步路由中的错误
我知道这个问题已经被提过很多次,也试过其他解决方案,现在请求帮忙调试。我配置了独立的errorHandler中间件,但抛出Error时Express没能捕获——终端能看到错误信息,但中间件完全没触发。
相关代码
error-handler.ts
import express, { Request, Response, NextFunction } from "express"; export const errorHandler = ( err: Error, req: express.Request, res: express.Response, next: express.NextFunction ) => { console.log('Something went wrong'); res.status(400).send({ message: err.message }); }
index.ts
import express, { Request, Response, NextFunction } from 'express'; import { json } from 'body-parser'; import { currentUserRouter } from './routes/current-user'; import { signinRouter } from './routes/signin'; import { signoutRouter } from './routes/signout'; import { signupRouter } from './routes/singup'; import { errorHandler } from './middlewares/error-handlers'; const app = express(); app.use(json()); app.use(currentUserRouter); app.use(signinRouter); app.use(signoutRouter); app.use(signupRouter); app.use(errorHandler); app.listen(3000, () => { console.log('Listening on port 3000!!!'); })
signup.ts
import express, { Request, Response } from "express"; const router = express.Router(); import { body, validationResult } from "express-validator"; const bodyValidation = [ body('email') .isEmail() .withMessage('Email must be valid'), body('password') .trim() .isLength({ min: 4, max: 20}) .withMessage('Password must be between 4 and 20 characters') ]; router.post('/api/users/signup', bodyValidation, async (req: Request, res: Response) => { const errors = validationResult(req); if(!errors.isEmpty()) { throw new Error('Invalid email or password'); } console.log('Creating a user...'); res.send({}); }); export { router as signupRouter }
问题原因
核心问题在于你的路由处理函数是异步函数:Express 默认不会自动捕获异步函数中抛出的错误。异步函数抛出的错误会被包装成 Promise 的 reject 状态,而 Express 本身不会处理未捕获的 Promise 拒绝,所以错误只会在终端打印,不会触发你的errorHandler中间件。
解决方案
方案1:用 try/catch 捕获错误并传递给 next
修改 signup.ts 的路由处理函数,添加 try/catch 块,在捕获错误后调用next(err)将错误传递给错误处理中间件:
import express, { Request, Response, NextFunction } from "express"; // 别忘了导入NextFunction const router = express.Router(); import { body, validationResult } from "express-validator"; const bodyValidation = [ body('email') .isEmail() .withMessage('Email must be valid'), body('password') .trim() .isLength({ min: 4, max: 20}) .withMessage('Password must be between 4 and 20 characters') ]; router.post('/api/users/signup', bodyValidation, async (req: Request, res: Response, next: NextFunction) => { try { const errors = validationResult(req); if(!errors.isEmpty()) { throw new Error('Invalid email or password'); } console.log('Creating a user...'); res.send({}); } catch (err) { next(err); // 把错误交给错误处理中间件 } }); export { router as signupRouter }
方案2:直接用 express-validator 返回错误(更简便)
既然已经用了 express-validator,没必要手动抛出错误,可以直接返回格式化的错误响应:
router.post('/api/users/signup', bodyValidation, async (req: Request, res: Response) => { const errors = validationResult(req); if(!errors.isEmpty()) { // 直接返回错误响应,无需经过全局错误中间件 return res.status(400).send({ errors: errors.array() }); } console.log('Creating a user...'); res.send({}); });
方案3:封装异步路由包装器(全局复用)
如果有大量异步路由,可以写一个通用的包装函数,自动捕获异步错误并传递给 next:
// 新建一个async-wrapper.ts文件 import { Request, Response, NextFunction } from "express"; export const asyncWrapper = (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) => { return (req: Request, res: Response, next: NextFunction) => { fn(req, res, next).catch(next); }; };
然后在 signup.ts 中使用:
import { asyncWrapper } from '../middlewares/async-wrapper'; // ... 其他代码不变 router.post('/api/users/signup', bodyValidation, asyncWrapper(async (req: Request, res: Response) => { const errors = validationResult(req); if(!errors.isEmpty()) { throw new Error('Invalid email or password'); } console.log('Creating a user...'); res.send({}); }));
内容的提问来源于stack exchange,提问作者jkalandarov
相关产品推荐
相关产品推荐

