Mongoose配置strict: "throw"时新增数据报错但更新不报错的原因及解决方法
Mongoose配置strict: "throw"时新增数据报错但更新不报错的原因及解决方法
嘿,这个问题我之前做用户中心项目的时候也碰到过,当时盯着代码反复试了好几次,后来翻了Mongoose的核心逻辑才搞清楚来龙去脉,给你拆解一下:
为什么会出现这种差异?
其实这是Mongoosestrict选项的设计逻辑导致的:
- 当你创建新文档时(比如
new User({foobar: "none"})然后调用save()),Mongoose会把你传入的整个对象和Schema做严格比对——毕竟这是初始化文档的核心步骤,strict: "throw"的规则会直接拦截所有不在Schema里的字段,立刻抛出错误。 - 但当你拿到已存在的文档实例,直接给它加额外属性(比如
u2.foobar = "none")再save(),情况就完全不同了:Mongoose的文档实例只会追踪Schema中定义的字段的变化,你手动添加的额外属性,Mongoose根本不会把它同步到数据库,自然也不会触发strict的校验逻辑——说白了,这时候你加的foobar只是挂在JS对象上的一个“冗余属性”,Mongoose完全没搭理它,所以不会报错,而且这个字段也压根不会被存到MongoDB里。
怎么让更新时也触发报错?
我整理了两种最实用的方法,能轻松实现你要的校验效果:
方法1:用set()方法代替直接赋值,带上严格校验选项
不要直接给实例加属性,改用Mongoose提供的set()方法来设置字段,同时传入strict: "throw"选项,这样就会触发严格校验:
let u2 = await User.findOne(); // 用set方法替代直接赋值 u2.set({ foobar: "none" }, { strict: "throw" }); await u2.save(); // <- 这里会像创建文档时一样抛出错误
方法2:使用批量更新方法并指定严格校验
如果你不需要先把实例拉到内存里,直接用findOneAndUpdate()这类更新方法的话,也可以在选项里加上strict: "throw",同样会触发字段校验:
await User.findOneAndUpdate( { _id: u2._id }, // 查询条件 { foobar: "none" }, // 要更新的内容 { strict: "throw" } // 开启严格校验 ); // <- 这里会直接抛出错误
如果你们团队有统一的开发规范,也可以在定义Schema的时候就把strict设为"throw",并且要求所有人都用set()方法修改字段,这样就能全局统一校验规则,避免有人不小心直接给实例加额外属性。
内容来源于stack exchange
相关产品推荐
相关产品推荐

