如何用express-async-handler处理Express异步错误及Mongoose ID格式问题
问题描述
我正在使用npm包express-async-handler,避免在路由控制器函数中使用try/catch块,同时配合自定义Express错误处理中间件。控制器中也使用了Mongoose模型。
控制器代码:
const getBootcamp = asyncHandler(async (req, res) => { const bootcamp = await Bootcamp.findById(req.params.id); if (bootcamp) { // 找到数据时正常返回(正常工作) return res.status(200).json({ success: true, data: bootcamp }); } else { // ID格式正确但不存在时,抛出404错误(正常工作) res.status(404); throw new Error('Bootcamp not found!'); } // 疑问:如何处理传入的ID不符合Mongoose ID格式的情况,例如"6414eec6e5db192719b46943000000000000000" });
自定义错误处理中间件代码:
const errorHandler = (error, req, res, next) => { const statusCode = res.statusCode === 200 ? 500 : res.statusCode; res.status(statusCode).json({ success: false, msg: error.message }); }; module.exports = errorHandler;
已通过app.use()在所有路由之后引入该错误中间件,配置正确。
我了解到需要调用next(err)将错误传递给错误中间件,但上述代码中else块之后的代码都是不可达的,请问该如何处理这类ID格式错误?
解决方案
方法1:提前校验ID格式
在调用findById之前,使用Mongoose内置的isValidObjectId方法校验ID格式,不符合则直接抛出错误:
const getBootcamp = asyncHandler(async (req, res) => { // 先校验ID格式 if (!mongoose.Types.ObjectId.isValid(req.params.id)) { res.status(400); throw new Error('Invalid Bootcamp ID format'); } const bootcamp = await Bootcamp.findById(req.params.id); if (bootcamp) { return res.status(200).json({ success: true, data: bootcamp }); } else { res.status(404); throw new Error('Bootcamp not found!'); } });
格式错误的请求会直接进入错误处理中间件,返回400状态码与对应提示。
方法2:在错误处理中间件中捕获Mongoose CastError
当传入ID格式不符合要求时,findById会抛出CastError,可在错误处理中间件中识别该错误类型并统一处理:
修改错误处理中间件:
const errorHandler = (error, req, res, next) => { let statusCode = res.statusCode === 200 ? 500 : res.statusCode; let message = error.message; // 处理Mongoose的ID格式错误 if (error.name === 'CastError') { message = `Resource not found with ID: ${error.value}`; statusCode = 404; // 也可根据业务需求改为400 } res.status(statusCode).json({ success: false, msg: message }); }; module.exports = errorHandler;
这种方式无需修改控制器代码,直接在中间层拦截格式错误,返回友好提示。
方法3:全局ID校验中间件
如果多个路由都需要校验ID格式,可编写全局中间件提前拦截所有带ID参数的请求:
// 全局ID校验中间件 const validateObjectId = (req, res, next) => { if (req.params.id && !mongoose.Types.ObjectId.isValid(req.params.id)) { res.status(400); throw new Error('Invalid ID format'); } next(); }; // 在路由中使用 router.get('/bootcamps/:id', validateObjectId, getBootcamp);
该方式避免了在每个控制器中重复编写校验逻辑,代码更简洁。
内容的提问来源于stack exchange,提问作者falah mahmood
相关产品推荐
相关产品推荐

