Mongoose 6.0.12下带必填字段模型的findOneAndUpdate报错如何解决
定位方法
首先在catch块中打印完整错误信息,查看err.message与err.errors字段,即可快速判断错误类型:
- 如果是语法类报错,基本是await与回调混用导致
- 如果是字段校验类报错,会明确标注哪个必填字段缺失
问题核心原因
- await与回调函数混用冲突
Mongoose 6.x版本开始不再支持同时使用await(Promise调用方式)和回调函数两种异步写法,你当前代码同时传入了回调参数又使用await修饰,会直接触发执行报错。 - upsert场景必填字段校验触发
你开启了upsert: true配置,如果执行更新时对应userId的用户不存在,会触发新建用户逻辑,此时你只传入了picture字段,pseudo、email、password等必填字段都未提供,Mongoose 6.x默认会校验所有schema规则,就会抛出校验失败错误。
解决方案
- 第一步:统一异步写法,移除回调函数,全部使用async/await+try/catch处理逻辑,修正后的代码如下:
try { const docs = await UserModel.findByIdAndUpdate( req.body.userId, { $set : {picture: "./uploads/profile/" + fileName}}, { new: true, upsert: true, setDefaultsOnInsert: true} ); return res.send(docs); } catch (err) { // 此处打印err可查看完整错误信息 console.log(err); return res.status(500).send({ message: err }); }
- 第二步:根据业务场景处理校验逻辑
- 如果你确认当前操作仅更新已存在的用户,不会触发新建逻辑,可以把
upsert: true改为upsert: false,避免触发必填字段校验 - 如果你确实需要支持upsert新建逻辑,可以使用
$setOnInsert操作符补充所有必填字段的插入默认值,示例如下:
const docs = await UserModel.findByIdAndUpdate( req.body.userId, { $set : {picture: "./uploads/profile/" + fileName}, // 只有触发upsert新建时才会执行该字段赋值 $setOnInsert: { pseudo: '新建用户默认用户名', email: '新建用户默认邮箱', password: '加密后的默认密码' } }, { new: true, upsert: true, setDefaultsOnInsert: true} );- 如果你不需要校验更新/插入时的字段规则,可以临时关闭校验(不推荐,会跳过所有schema规则校验,容易产生脏数据),配置参数如下:
{ new: true, upsert: true, setDefaultsOnInsert: true, runValidators: false } - 如果你确认当前操作仅更新已存在的用户,不会触发新建逻辑,可以把
内容的提问来源于stack exchange,提问作者ilyes amen
相关产品推荐
相关产品推荐

