我正在使用Postman测试我正在为项目工作的API。我发送
{
"fullName": "Smellydog",
"emailAddress": "SmellydogCoding@gmail.com",
"password": "password",
"confirmPassword": "password"
}
作为此API路径的请求主体
users.post('/', (req, res, next) => {
let user = new Users(req.body);
user.save((error,user) => {
if (error) {
return next(error);
} else {
res.status = 201;
res.location('/');
res.end();
}
});
});
这是我的用户架构
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const UserSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
trim: true,
},
emailAddress: {
type: String,
required: true,
unique: true,
match: [emailRegex, "Please enter a valid email address"],
trim: true
},
password: {
type: String,
required: true
},
confirmPassword: {
type: String,
required: true
}
});
// hash password before saving to database
UserSchema.pre('save', function(next) {
bcrypt.hash(this.password, 10, function(error, hash) {
if (error) {
return next(error);
} else {
this.password = hash;
this.confirmPassword = hash;
next();
}
});
});
UserSchema.pre('validate', function(next) {
let user = this;
if (user.password !== user.confirmPassword) {
return next('Passwords must match');
} else {
next();
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
预保存挂钩应该加密密码,然后将其保存到密码和confirmPassword字段。如果我使用Chrome调试器在预保存挂钩的末尾设置断点(其中调用next()),并检查this.password和this.confirmPassword,我看到它们被设置为新创建的散列,但是当我检查数据库之后,这两个字段都设置为原始密码字符串
{
"_id": "58d835f0d026194610578c74",
"fullName": "Smellydog",
"emailAddress": "SmellydogCoding@gmail.com",
"password": "password",
"confirmPassword": "password",
"__v": 0
}
答案 0 :(得分:2)
我同意odoreDogCoding先前的回答。该解决方案起作用的原因是因为它的上下文。当执行线程上线时:
let user =this
“ this”的上下文是指正在创建的文档的实例。但是,一旦输入bcrypt hash方法的执行上下文,“ this”上下文将不再引用我们正在数据库中创建的文档的实例,而是引用函数的范围。
通过将对我们正在创建的文档对象的实例的引用保存在变量user中,我们保留了对要在散列后更新的this的引用。而且由于我们只是在进行浅表复制(实际上是对原始this的引用),所以当我们更新用户对象上的属性“ password”时,我们也在更新“ this”文档对象上的属性“ password”
答案 1 :(得分:0)
我添加了
let user = this;
到预保存挂钩的第一行,然后将字段称为
user.password
user.confirmPassword
喜欢这样
// hash password before saving to database
UserSchema.pre('save', function(next) {
let user = this;
bcrypt.hash(user.password, 10, function(error, hash) {
if (error) {
return next(error);
} else {
user.password = hash;
user.confirmPassword = hash;
next();
}
});
});
似乎this.password正在预保存挂钩内部进行更新,但最后没有持久保存到数据库。将此设置为变量似乎改变了Paolo建议的上下文。我真的很想知道如何以及为什么这样做。
答案 2 :(得分:0)
这是因为您正在使用
bcrypt.hash(this.password, 10, function(error, hash)
.hash
方法是异步的,这意味着您在散列密码之前正在运行next()
函数。
改为使用异步/等待
UserSchema.pre('save', async function (next) {
// check if password is present and is modified.
try {
if (this.password && this.isModified('password')) {
this.password = await bcrypt.hash(this.password, passwordSaltRound);
}
next();
} catch (err) {
next(err);
}
});
使用您的方法,您可以使用bcrypt.hashSync(this.password, 10, function(error, hash)
,但它将创建阻止代码!
所以只需使用async / await方法<3
答案 3 :(得分:0)
我知道这是一个老问题,但我想说另一个解决方案是使用 ES6 中引入的箭头函数表达式。
UserSchema.pre('save', function(next) {
bcrypt.hash(this.password, 10, (error, hash) => {
if (error) {
return next(error);
} else {
this.password = hash;
this.confirmPassword = hash;
next();
}
});
});
这样做的原因是因为箭头函数表达式没有自己的 this
绑定,所以它从定义函数的地方获取上下文;在这种情况下,这是用户对象的上下文。
您可以在 MDN Web Docs 上阅读更多关于箭头功能的信息here