Mongoose更新密码时findOneAndUpdate钩子报this.isModified不是函数怎么办
报错原因
- Mongoose 中
findOneAndUpdate(findByIdAndUpdate底层调用该方法)的 pre 钩子中,this指向的是查询 Query 对象,而非 MongoDB 文档实例。isModified()是仅文档实例具备的方法,因此直接调用会抛出不存在的错误。
解决方案
方案1:修改pre钩子适配查询操作
该方案无需修改现有接口逻辑,只需调整findOneAndUpdate钩子内的判断逻辑,通过查询对象提供的getUpdate()方法获取待更新字段,判断密码是否需要更新后加密即可:
// 加密更新操作中的密码字段 userSchema.pre('findOneAndUpdate', async function (next) { const updateData = this.getUpdate() // 兼容普通更新和$set更新两种写法 const rawPassword = updateData.password || updateData.$set?.password if (rawPassword) { const encryptedPassword = await bcrypt.hash(rawPassword, 10) // 把加密后的密码塞回更新数据 if (updateData.$set) { updateData.$set.password = encryptedPassword } else { updateData.password = encryptedPassword } } next() })
方案2:改为文档实例操作+save钩子
该方案更符合Mongoose的常规设计,先查询得到文档实例,修改字段后调用save()方法,可直接使用isModified方法做判断:
调整接口代码
exports.updateProfileUser = catchAsyncErrors(async (req, res) => { const userId = req.params.id const { username, password } = req.body let user = await User.findById(userId) if (!user) { return res.status(404).json({ success: false, message: '用户不存在' }) } // 手动赋值更新字段 user.username = username if (password) { user.password = password } // 调用save触发保存钩子 await user.save() res.status(200).json({ success: true, user, message: 'User Profile Updated', }) })
调整钩子为save前置钩子
// 加密保存前的密码字段 userSchema.pre('save', async function (next) { if (!this.isModified('password')) { return next() } this.password = await bcrypt.hash(this.password, 10) next() })
注意:方案2会触发所有和save操作相关的校验、钩子逻辑,业务逻辑复杂的场景更推荐使用该方案。
内容的提问来源于stack exchange,提问作者Muammar HM
相关产品推荐
相关产品推荐

