Mongoose Schema配置的required必填校验未生效如何解决
校验未生效的核心原因
- Mongoose的Schema校验默认仅在
create()、save()这类文档新建/全量保存操作中自动触发,updateOne、updateMany、findOneAndUpdate等更新类方法默认关闭校验规则,因此直接调用时你配置的required规则根本不会执行。 - 即便手动开启更新操作的校验,Mongoose对更新操作的校验逻辑和新建文档的逻辑存在差异:更新校验默认只会校验你显式传入更新语句的字段,对于未传入的字段,哪怕标记了
required: true,在upsert: true触发新文档插入时也不会触发校验——因为Mongoose默认将更新操作判定为「修改部分字段」,不会要求未涉及的字段满足必填规则。
修复方案
根据业务场景选一种即可:
方案1:保留upsert原子操作,补全校验配置+前置校验
这种方案不会破坏现有updateOne的原子性,同时能让必填校验正常生效:
- 给updateOne的第三个配置参数加
runValidators: true,开启更新操作的校验 - 在执行updateOne前,基于传入的item创建临时模型实例,调用Mongoose原生的validate方法做全量校验,校验通过后再执行更新操作
修改后的代码示例:
async function addReviewer(item) { try { // 先做全量Schema校验,覆盖upsert插入时的必填校验场景 const tempDoc = new reviewerModal(item); await tempDoc.validate(); // 校验通过后再执行原子upsert操作 await reviewerModal.updateOne({ email: item.email }, item, { upsert: true, runValidators: true, // 开启更新场景下的字段校验 }); } catch (err) { console.log(err); // 按需抛错给上层处理 throw err; } }
方案2:改用find+save逻辑,完全复用Schema默认校验
如果对原子性要求不高,这种写法最稳妥,不会踩Mongoose更新校验的特殊逻辑坑,所有Schema配置的校验(包括required、自定义校验、默认值等)都会和create操作表现完全一致:
async function addReviewer(item) { try { // 先查询对应邮箱的记录 const existReviewer = await reviewerModal.findOne({ email: item.email }); if (existReviewer) { // 存在则更新字段后保存 Object.assign(existReviewer, item); await existReviewer.save(); return existReviewer; } // 不存在则新建后保存,自动触发全量校验 const newReviewer = new reviewerModal(item); await newReviewer.save(); return newReviewer; } catch (err) { console.log(err); throw err; } }
注意:不要尝试仅靠加
runValidators: true解决问题,这个配置只能校验你传入更新语句的字段,比如你传入的item缺了reviewerId字段,哪怕开了runValidators,upsert插入时也不会报reviewerId必填的错误,必须配合全量校验或者改用save逻辑才能覆盖所有必填字段的校验。
内容的提问来源于stack exchange,提问作者Aman Srivastava
相关产品推荐
相关产品推荐

