Mongoose pre('update')中间件失效求助:修改密码时无法加密
解决Mongoose pre('update')中间件不生效的问题
嘿,我来帮你搞定这个Mongoose中间件的问题!先给你拆解下问题根源,再给你靠谱的解决方案:
为啥pre('update')不生效?
你得先搞懂Mongoose里这两个钩子的本质区别:
pre('save')是针对单个文档实例的操作,比如你新建用户或者先查出来文档再修改保存,this直接指向那个文档对象,所以你能直接修改this.password,加密逻辑自然生效。- 但
pre('update')(包括updateOne、findOneAndUpdate这类批量/单条更新操作)不一样,这里的this是查询操作对象,不是你要更新的文档实例。而且默认情况下,这类钩子不会像save那样触发文档级的处理,再加上你代码里直接用了未定义的password变量,这就导致加密逻辑根本没跑起来。
另外你原代码里在pre('update')里调用this.update()的写法也有问题,这会触发无限循环,完全没必要这么做。
靠谱的解决方案
我推荐两种方案,按需选择:
方案1:用pre('findOneAndUpdate')(更常用)
如果你是用findByIdAndUpdate或者findOneAndUpdate来修改用户资料(这是最常见的场景),用这个钩子就对了。它能让你获取到要更新的数据,然后直接修改加密:
userSchema.pre('findOneAndUpdate', function(next) { // 从更新操作里拿到新密码 const newPassword = this.getUpdate().$set?.password; if (newPassword) { // 加密后替换原更新值 this.getUpdate().$set.password = bcrypt.hashSync(newPassword, bcrypt.genSaltSync(8)); } next(); });
方案2:修复pre('update')的写法
如果你一定要用pre('update'),得从查询对象的_update属性里拿更新数据,调整成这样:
userSchema.pre('update', function(next) { const newPassword = this._update.$set?.password; if (newPassword) { this._update.$set.password = bcrypt.hashSync(newPassword, bcrypt.genSaltSync(8)); } next(); });
额外提醒
- 如果你是先把用户文档查出来,修改密码后再调用
save(),那触发的是pre('save'),你的原逻辑本来就没问题,不用改。 - 加密时可以去掉第三个
null参数,bcrypt.hashSync只需要密码和盐值两个参数就够了。
内容的提问来源于stack exchange,提问作者Scott Kim
相关产品推荐
相关产品推荐

