如何在Schema.pre('update')中间件中传递密码字段进行哈希处理
嘿,这个问题我熟!Mongoose里的update类中间件确实和save中间件不一样,踩过坑的人来给你捋清楚怎么处理密码哈希~
首先得明确:pre('update')这类中间件里,this指向的是查询对象,不是文档实例,所以你没法直接用this.password拿到要更新的密码。得通过this.getUpdate()方法获取到当前要执行的更新数据,再从中提取密码字段进行哈希处理。
下面分两种最常见的场景给你代码示例:
1. 处理findOneAndUpdate(最常用的单文档更新)
如果你用的是findOneAndUpdate来更新用户信息,中间件可以这么写:
const userSchema = new mongoose.Schema({ email: { type: String, required: true }, password: { type: String, required: true } }); // 绑定findOneAndUpdate的前置中间件 userSchema.pre('findOneAndUpdate', async function(next) { // 获取本次更新的全部数据 const updateData = this.getUpdate(); // 检查更新数据里是否包含password字段 if (updateData.password) { // 调用你已定义的哈希函数(这里用bcrypt举例子,替换成你的函数就行) updateData.password = await bcrypt.hash(updateData.password, 10); } // 如果是用$set语法更新(比如{ $set: { password: 'newPass' } }),要这么检查 else if (updateData.$set?.password) { updateData.$set.password = await bcrypt.hash(updateData.$set.password, 10); } next(); // 一定要调用next,不然请求会卡住 });
2. 处理updateOne/updateMany批量或单文档更新
如果用的是updateOne或updateMany,逻辑类似,但要注意这类操作更常用$set语法,所以要兼顾两种更新格式:
async function hashUpdatePassword(next) { const updateData = this.getUpdate(); // 处理直接设置password的情况:{ password: 'newPass' } if (updateData.password) { updateData.password = await bcrypt.hash(updateData.password, 10); } // 处理用$set设置password的情况:{ $set: { password: 'newPass' } } else if (updateData.$set?.password) { updateData.$set.password = await bcrypt.hash(updateData.$set.password, 10); } next(); } // 把同一个哈希逻辑绑定到多个更新类中间件 userSchema.pre('updateOne', hashUpdatePassword); userSchema.pre('updateMany', hashUpdatePassword);
几个关键提醒
- 别用
pre('update'):这个是旧版Mongoose针对Model.update()方法的中间件,现在官方推荐用updateOne/findOneAndUpdate这类更明确的方法,对应的中间件行为更稳定。 - 哈希函数要异步处理:如果你的哈希函数是异步的(比如bcrypt.hash),一定要加
await,不然会把Promise对象存到数据库里,而不是哈希后的字符串。 - 覆盖所有更新场景:把同一个哈希逻辑抽成复用函数,绑定到所有需要的更新中间件上,避免重复代码。
这样设置后,只要更新操作里包含password字段,中间件就会自动对其进行哈希处理,再存入数据库啦~
内容的提问来源于stack exchange,提问作者Rupesh
相关产品推荐
相关产品推荐

