You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 11:54:03