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

Mongoose pre save中间件未触发,密码未哈希存入数据库

问题分析与修复方案

核心问题点

  1. 数据库连接未等待完成:调用dbConnect()时未使用await,导致模型操作在数据库连接建立前执行,Mongoose中间件无法正常触发。
  2. 模型复用导致中间件未绑定:models.Users || model(...)的写法在Next.js热重载场景下,可能复用了未绑定最新中间件的旧模型实例。
  3. save方法混合异步写法:同时使用await和回调函数,可能导致执行逻辑异常。
  4. 中间件错误处理不完整: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);
});

验证方法

  1. 重启Next.js开发服务器,确保模型重新加载。
  2. 添加新用户时,检查控制台是否打印Pre-Save Hash has fired.。
  3. 查看数据库中用户的password字段是否为哈希值(长度约60位的字符串)。

内容的提问来源于stack exchange,提问作者michael

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:25:27