使用bcrypt时出现Object is undefined问题的原因及解决方法
问题分析与解决方案
我来帮你梳理下这个问题的核心原因和修复方法:
为什么绑定函数的对象是undefined?
你在mongoose的pre('save')中间件里用了箭头函数作为回调,这就是问题的根源!箭头函数不会创建自己的this上下文,它会捕获外层作用域的this,而mongoose原本会把pre中间件的this绑定到当前要保存的文档对象上。用箭头函数的话,this就不再指向文档,变成了undefined(或者外层的无关上下文),自然没法处理密码哈希。
看你写的这段代码:
UserSchema.pre('save',(next)...
箭头函数在这里完全不适用,得换成普通函数才行。
修复步骤&代码示例
1. 把中间件的箭头函数改成普通函数
这样this就会正确指向当前的user文档,我们就能拿到要哈希的密码了。同时结合bcrypt完成密码哈希,注意要处理异步流程:
const bcrypt = require('bcrypt'); const mongoose = require('mongoose'); // 先定义你的UserSchema const UserSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true }, email: { type: String, required: true, unique: true } }); // 替换箭头函数为普通函数! UserSchema.pre('save', function(next) { const user = this; // 只有当密码被修改(或新用户)时才哈希,避免更新其他字段时重复哈希 if (!user.isModified('password')) { return next(); } // 生成盐值,成本因子设为10(平衡速度和安全性) bcrypt.genSalt(10, (err, salt) => { if (err) return next(err); // 用盐值哈希密码 bcrypt.hash(user.password, salt, (err, hash) => { if (err) return next(err); // 把原密码替换成哈希后的结果 user.password = hash; next(); // 继续执行保存流程 }); }); }); // 创建User模型 const User = mongoose.model('User', UserSchema);
2. 优化你的路由代码(可选但推荐)
原路由里的错误处理可以更精准,比如利用mongoose的重复键错误码(11000)来判断用户名/邮箱重复:
app.post('/users', (req, res) => { const user = new User({ username: req.body.username, password: req.body.password, email: req.body.email }); user.save() .then(() => res.send("User Created Successfully")) .catch(err => { // 捕获MongoDB的唯一索引冲突错误 if (err.code === 11000) { res.send('Username or email already exists'); } else { // 其他错误返回500状态码 res.status(500).send('Server error: ' + err.message); } }); });
额外注意点
- 永远不要在mongoose的文档中间件(pre/post save等)里用箭头函数,除非你明确不需要访问文档对象的
this。 - 一定要判断
isModified('password'),否则用户更新其他字段(比如邮箱)时,密码会被重新哈希,导致下次登录失败。 - bcrypt的操作是异步的,必须在哈希完成后调用
next(),否则文档会在密码哈希前就被保存,等于没做哈希处理。
内容的提问来源于stack exchange,提问作者user9445158
相关产品推荐
相关产品推荐

