mongoose回调报错时如何直接终止express路由函数执行
Mongoose 异步错误处理解决方案
核心问题原因
你当前的代码同时混用了await和回调写法,当你给findOne传入回调函数时,Mongoose不会返回Promise对象,外层的await不会产生任何等待效果,且回调里的return只能退出回调函数本身,无法终止外层路由函数的执行,因此后续逻辑一定会被触发。
推荐实现方案(无需手动编写try/catch)
方案1:Promise链式调用+全局错误中间件
Mongoose查询方法不传回调时默认返回Promise,直接用.then()处理正常逻辑,.catch()统一把错误传给next,搭配Express全局错误处理中间件即可:
router.route('/addSomething').post(async (req,res,next) => { await Document.findOne({name: req.body.name}) .then(doc => { if (doc) throw new Error("This name already exists") }) .catch(next) // 所有错误直接传给全局错误处理中间件 // 走到此处说明无错误,直接执行后续逻辑 await Document.save() // 其余后续逻辑... })
在所有路由最后添加全局错误处理中间件,统一处理所有抛出的错误:
app.use((err, req, res, next) => { res.status(err.status || 500).json({ message: err.message }) })
方案2:高阶函数统一封装异步路由
如果更偏好await的写法,可以写一个统一的错误捕获wrapper,所有异步路由的错误都会被自动捕获,完全不需要在单个路由里写try/catch:
// 统一错误捕获高阶函数 const asyncWrapper = (fn) => { return (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next) } } // 路由用wrapper包裹即可 router.route('/addSomething').post(asyncWrapper(async (req, res, next) => { const doc = await Document.findOne({name: req.body.name}) if (doc) throw new Error("This name already exists") // 后续逻辑 await Document.save() // 其余逻辑... }))
额外优化建议
如果名称唯一性是该集合的通用规则,可以直接在Mongoose Schema中给name字段加唯一索引,不需要手动写查询校验逻辑:
const documentSchema = new mongoose.Schema({ name: { type: String, unique: true, // 唯一索引,插入重复值会自动抛出错误 required: true } // 其余字段定义 })
添加唯一索引后,重复插入name时Mongoose会自动抛出错误码为11000的错误,在全局错误中间件里统一处理即可:
app.use((err, req, res, next) => { if (err.code === 11000) { err.message = "This name already exists" err.status = 400 } res.status(err.status || 500).json({ message: err.message }) })
内容的提问来源于stack exchange,提问作者Edward Martin
相关产品推荐
相关产品推荐

