Express API中无法捕获并处理Mongoose/MongoDB错误
解决MongoDB重复键错误无法捕获及前端提示问题
问题核心
想要让前端感知到重复提交ID的验证错误,但代码中的异常捕获逻辑完全失效,MongoDB的重复键错误没进入自定义的catch块,最终触发了未处理的Promise拒绝警告,无法将错误信息反馈给前端。
错误原因
你在await finishedjob.save()后链式调用了.catch(),这个操作会吞噬Promise的拒绝状态,把它转换成已解决的Promise,导致外层的try/catch无法捕获到错误,最终触发全局的未处理Promise拒绝提示。
修复方案
方案1:移除链式.catch(),用try/catch统一捕获
删掉.catch(),让await抛出的错误直接进入外层try/catch块,这样就能正常捕获重复键错误,再给前端返回对应提示:
exports.createFinishedJob = async (req, res) => { try { console.log('createFinishedJob req.body', req.body); const finishedjob = new FinishedJob(); if (req.body.jobSubmissionId) { finishedjob.jobSubmissionId = req.body.jobSubmissionId }; if (req.body.customerId) { finishedjob.customerId = new ObjectId(req.body.customerId) }; finishedjob.finishedjob = req.body.finishedjob; if (req.body.other) { finishedjob.other = req.body.other }; console.log('createFinishedJob', finishedjob); // 移除.catch(),让错误进入try/catch await finishedjob.save(); // 保存成功返回数据 return res.status(201).json(finishedjob); } catch (err) { console.error("createFinishedJob error", err); // 判断是否为MongoDB重复键错误 if (err.code === 11000) { return res.status(409).json({ message: `提交ID ${err.keyValue.jobSubmissionId} 已存在,请勿重复提交` }); } // 其他错误返回通用提示 return res.status(500).json({ message: "创建失败,请稍后重试" }); } };
方案2:保留.catch()但重新抛出错误
如果需要在链式调用里做日志,可以在.catch()处理后重新抛出错误,让外层try/catch捕获:
exports.createFinishedJob = async (req, res) => { try { console.log('createFinishedJob req.body', req.body); const finishedjob = new FinishedJob(); if (req.body.jobSubmissionId) { finishedjob.jobSubmissionId = req.body.jobSubmissionId }; if (req.body.customerId) { finishedjob.customerId = new ObjectId(req.body.customerId) }; finishedjob.finishedjob = req.body.finishedjob; if (req.body.other) { finishedjob.other = req.body.other }; console.log('createFinishedJob', finishedjob); try { await finishedjob.save() .catch(err => { console.log("HALLO err", err); // 重新抛出错误,让外层try/catch捕获 throw err; }); return res.status(201).json(finishedjob); } catch (err) { console.error("createFinishedJob error", err); if (err.code === 11000) { return res.status(409).json({ message: `提交ID ${err.keyValue.jobSubmissionId} 已存在,请勿重复提交` }); } return res.status(500).json({ message: "创建失败,请稍后重试" }); } } catch (err) { console.error("Something went wrong") console.error(err) return res.status(500).json({ message: "系统异常,请稍后重试" }); } };
额外说明
Mongoose的unique: true是用来创建MongoDB唯一索引的,不属于Schema验证逻辑,所以重复键错误是MongoDB层面抛出的,需要通过错误码11000判断。如果要在Schema层面做前置验证,可以添加自定义验证器:
jobSubmissionId:{ type: String, unique: true, index: true, validate: { validator: async function(value) { const count = await this.constructor.countDocuments({ jobSubmissionId: value }); return count === 0; }, message: props => `提交ID ${props.value} 已存在` } }
注意:这种前置验证在高并发场景下可能存在竞态问题,最终还是要依赖MongoDB的唯一索引保证数据一致性,建议两种方式结合使用。
内容的提问来源于stack exchange,提问作者urfx
相关产品推荐
相关产品推荐

