Mongoose pre save中间件未触发,密码未哈希存入数据库
问题分析与修复方案
核心问题点
- 数据库连接未等待完成:调用
dbConnect()时未使用await,导致模型操作在数据库连接建立前执行,Mongoose中间件无法正常触发。 - 模型复用导致中间件未绑定:
models.Users || model(...)的写法在Next.js热重载场景下,可能复用了未绑定最新中间件的旧模型实例。 save方法混合异步写法:同时使用await和回调函数,可能导致执行逻辑异常。- 中间件错误处理不完整:bcrypt回调中仅打印错误但未调用
next(err),会导致中间件流程卡住;且未判断密码是否修改,可能重复哈希。
修复步骤
1. 修正数据库连接逻辑
在处理函数中等待数据库连接完成:
// 添加用户的处理函数 import dbConnect from '@/utils/mongodb'; import UserModel from '@/models/user.model'; import { NextApiRequest, NextApiResponse } from 'next'; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { try { // 等待数据库连接完成 await dbConnect(); const query = req.body; const newUser = new UserModel(query); // 统一使用async/await,移除回调 const addedUser = await newUser.save(); res.status(200).json(addedUser); } catch (error) { console.error(error); res.status(500).json({ message: 'Internal server error' }); } }
2. 确保模型中间件正确绑定
修改用户模型文件,确保每次创建模型时都使用绑定了中间件的Schema:
// users.model文件 import { models, model, Schema } from 'mongoose'; import bcrypt from 'bcrypt'; const UserSchema: Schema = new Schema({ email: { type: String, // 改用标准的String构造函数,而非字符串"String" required: true, unique: true, }, password: { type: String, required: true, }, displayName: { type: String, required: true, }, role: { type: String, }, }); // 优化中间件:判断密码是否修改,避免重复哈希;完善错误处理 UserSchema.pre('save', function (next) { console.log('Pre-Save Hash has fired.'); const user = this as any; // 如果密码未修改,直接跳过哈希 if (!user.isModified('password')) return next(); bcrypt.genSalt(10, (err, salt) => { if (err) return next(err); // 传递错误给next,终止流程 bcrypt.hash(user.password, salt, (err, hash) => { if (err) return next(err); user.password = hash; next(); }); }); }); // 修复热重载时的模型复用问题:如果模型已存在,先删除再重新创建 if (models.Users) { delete models.Users; } const UserModel = model('Users', UserSchema, 'users'); export default UserModel;
3. 可选:改用async/await风格的中间件
Mongoose 6+支持async/await风格的中间件,代码更简洁且不易出错:
UserSchema.pre('save', async function () { console.log('Pre-Save Hash has fired.'); const user = this as any; if (!user.isModified('password')) return; const salt = await bcrypt.genSalt(10); user.password = await bcrypt.hash(user.password, salt); });
验证方法
- 重启Next.js开发服务器,确保模型重新加载。
- 添加新用户时,检查控制台是否打印
Pre-Save Hash has fired.。 - 查看数据库中用户的
password字段是否为哈希值(长度约60位的字符串)。
内容的提问来源于stack exchange,提问作者michael
相关产品推荐
相关产品推荐

