迁移邮箱验证器至validators.js后MongoDB findOne失效修复
修复MongoDB邮箱查重验证迁移后的查询失效问题
将邮箱查重逻辑从auth.js迁移至validators.js后,User.findOne()无法正常查询用户,但用户注册的save功能仍可正常工作,核心问题出在模型引用错误、异步操作未正确处理以及模型未模块化导出三个方面。
问题根源
- 模型引用错误:
validators.js中const User = require('./auth')导入的是auth.js导出的路由对象(router),而非mongoose的User模型,导致调用的findOne并非数据库查询方法。 - 异步操作未等待:
User.findOne()是异步Promise操作,未加await直接判断existingUser,此时existingUser为Promise对象(属于真值),会导致无论邮箱是否存在都抛出错误,且实际未执行有效查询。 - 模型未对外导出:
auth.js中定义的User模型仅在内部使用,未对外导出,无法被其他模块正确引用。
修复步骤
步骤1:抽离User模型到独立文件
新建models/User.js文件,统一管理User模型,避免重复定义和引用混乱:
const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ email: String, password: String, passwordConfirmation: String, }); module.exports = mongoose.model('User', userSchema);
步骤2:修正validators.js的模型引用与异步处理
修改validators.js,导入正确的User模型,并在验证器中用await等待异步查询结果:
const { check } = require('express-validator'); // 导入统一的User模型 const User = require('../models/User'); module.exports = { requireEmail: check('email') .trim() .normalizeEmail() .isEmail() .withMessage('Must be a valid email') .custom(async (email) => { // 等待异步查询完成 const existingUser = await User.findOne({ email: email }); if (existingUser) { throw new Error('Email in use'); } }), requirePassword: check('password') .trim() .isLength({ min: 4, max: 20 }) .withMessage('Must be between 4 and 20 characters'), requirePasswordConfirmation: check('passwordConfirmation') .trim() .isLength({ min: 4, max: 20 }) .withMessage('Must be between 4 and 20 characters') .custom(async (passwordConfirmation, { req }) => { if (passwordConfirmation !== req.body.password) { throw new Error('Passwords must match'); } }), };
步骤3:更新auth.js的模型引用与逻辑顺序
修改auth.js,导入抽离后的User模型,移除内部重复定义的模型,并优化异步逻辑顺序(先检查验证错误再执行加密与保存):
const express = require('express'); const { check, validationResult } = require('express-validator'); const bcrypt = require('bcrypt'); const saltRounds = 10; const signupTemplate = require('../../views/admin/auth/signup'); const signinTemplate = require('../../views/admin/auth/signin'); const { requireEmail, requirePassword, requirePasswordConfirmation, } = require('./validators'); // 导入统一的User模型 const User = require('../models/User'); const router = express.Router(); router.get('/signup', (req, res) => { res.send(signupTemplate({ req })); }); router.post( '/signup', [requireEmail, requirePassword, requirePasswordConfirmation], async (req, res) => { try { // 先检查验证错误 const errors = validationResult(req); if (!errors.isEmpty()) { return res.send(signupTemplate({ errors: errors.array() })); } // 异步加密密码 const hash = await bcrypt.hash(req.body.password, saltRounds); // 创建并保存用户 const newUser = new User({ email: req.body.email, password: hash, }); await newUser.save(); console.log('success'); res.send('Account created!!!'); } catch (err) { console.log(err); res.send('Error creating account'); } } ); module.exports = router;
补充说明
- 抽离模型是Node.js + MongoDB开发的最佳实践,能有效避免重复定义和跨模块引用错误。
- 所有异步数据库操作必须用
await等待结果,否则无法获取实际查询/操作数据。 - 优化后的
auth.js改用async/await替代嵌套回调,代码可读性更强,逻辑顺序更合理(先验证再执行核心操作)。
内容的提问来源于stack exchange,提问作者rachel
相关产品推荐
相关产品推荐

