Express错误处理中间件无法正常捕获InvalidKeyError问题
解决Express中Async路由抛出的自定义错误无法被错误处理中间件捕获的问题
问题根源
Express 默认不会自动捕获异步路由处理函数中抛出的错误,这些错误会以未处理的 Promise 拒绝形式存在,进而导致应用崩溃,而不会被错误处理中间件捕获。
反观全局路由中的 NotFoundError 能正常被捕获,是因为 app.all('*') 的处理函数是同步函数,Express 会自动捕获同步函数抛出的错误并传递给错误处理中间件。
解决方案
方案1:使用 try/catch 手动捕获错误并传递给 next
修改你的 checkAPIKeyRouter 代码,在异步函数内部用 try/catch 包裹逻辑,捕获错误后通过 next(err) 传递给错误处理中间件:
import express from 'express'; import { Request, Response, NextFunction } from 'express'; import { InvalidKeyError } from '../errors/invalid-api-key-error'; import { APIKey } from '../models/api-key-model'; import { Hash } from '../services/hash'; const router = express.Router(); router.get('/api/checkkey', async (req: Request, res: Response, next: NextFunction) => { try { const suppliedKey = req.get('key'); const suppliedSecret = req.get('secret'); if (!suppliedKey || !suppliedSecret) throw new InvalidKeyError('Key or secret not passed'); const storedAPIKey = await APIKey.findOne({ key: suppliedKey }); if (!storedAPIKey) throw new InvalidKeyError('Key does not exist'); if (!Hash.compareKeys(suppliedKey, suppliedSecret, storedAPIKey.salt)) throw new InvalidKeyError('Key and secret do not correspond') res.send('Hi there'); } catch (err) { next(err); // 将错误传递给错误处理中间件 } }); export { router as checkAPIKeyRouter };
方案2:创建通用异步错误处理包装函数
如果项目中有多个异步路由,推荐封装一个通用的包装函数,自动处理异步错误的传递,避免重复写 try/catch:
首先创建一个工具函数(比如 utils/async-handler.ts):
import { Request, Response, NextFunction } from 'express'; 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); }; };
然后在路由中使用这个包装函数:
import express from 'express'; import { InvalidKeyError } from '../errors/invalid-api-key-error'; import { APIKey } from '../models/api-key-model'; import { Hash } from '../services/hash'; import { asyncHandler } from '../utils/async-handler'; const router = express.Router(); router.get('/api/checkkey', asyncHandler(async (req, res) => { const suppliedKey = req.get('key'); const suppliedSecret = req.get('secret'); if (!suppliedKey || !suppliedSecret) throw new InvalidKeyError('Key or secret not passed'); const storedAPIKey = await APIKey.findOne({ key: suppliedKey }); if (!storedAPIKey) throw new InvalidKeyError('Key does not exist'); if (!Hash.compareKeys(suppliedKey, suppliedSecret, storedAPIKey.salt)) throw new InvalidKeyError('Key and secret do not correspond') res.send('Hi there'); })); export { router as checkAPIKeyRouter };
方案3:升级到 Express 5+(可选)
Express 5 版本开始会自动捕获异步路由处理函数中抛出的错误,并将其传递给错误处理中间件,无需手动处理。如果项目允许升级依赖,可以直接升级 Express 版本。
验证修改
完成上述任一修改后,再次触发 InvalidKeyError,错误会被错误处理中间件正常捕获,并返回和 NotFoundError 一致的规范 JSON 响应。
内容的提问来源于stack exchange,提问作者Papaya
相关产品推荐
相关产品推荐

