Короче говоря, вы забыли, что входите в функцию обратного вызова, которая имеет другую функциональную область, и вы все еще ссылаетесь на this
, который в то время фактически не был экземпляром "модели".
Чтобы исправить это, сделайте копию this
, прежде чем делать что-то вроде запуска другой функции с обратным вызовом:
UserSchema.pre('save', function(next) {
var user = this; // keep a copy
if (this.isNew) {
bcrypt.genSalt(10, function(err,salt) {
if (err) next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) next(err);
user.password = hash;
next();
});
});
}
});
Альтернативный подход, конечно, заключается в модернизации вещей и использовании Promise
результатов с async/await
. Библиотека bcrypt
, которая на самом деле является «ядром», а не форком, делает это прямо из коробки:
UserSchema.pre('save', async function() {
if (this.isNew) {
let salt = await bcrypt.genSalt(10);
let hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
});
Помимо того, что современный подход в целом является более чистым кодом, вам также не нужно менять область действия this
, поскольку мы не «погружаемся» в вызов другой функции. Все меняется в той же области, и, конечно, ждет продолжения асинхронных вызовов, прежде чем продолжить.
Полный пример - обратный вызов
const { Schema } = mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const uri = 'mongodb://localhost/crypto';
var userSchema = new Schema({
email: String,
password: String
});
userSchema.pre('save', function(next) {
var user = this; // keep a copy
if (this.isNew) {
bcrypt.genSalt(10, function(err,salt) {
if (err) next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) next(err);
user.password = hash;
next();
});
});
}
});
const log = data => console.log(JSON.stringify(data, undefined, 2));
const User = mongoose.model('User', userSchema);
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await User.create({ email: 'ted@example.com', password: 'password' });
let result = await User.findOne();
log(result);
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Полный пример - Promise async / await
const { Schema } = mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const uri = 'mongodb://localhost/crypto';
var userSchema = new Schema({
email: String,
password: String
});
userSchema.pre('save', async function() {
if (this.isNew) {
let salt = await bcrypt.genSalt(10);
let hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
});
const log = data => console.log(JSON.stringify(data, undefined, 2));
const User = mongoose.model('User', userSchema);
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await User.create({ email: 'ted@example.com', password: 'password' });
let result = await User.findOne();
log(result);
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Оба кода показывают правильно зашифрованный пароль, поскольку мы фактически устанавливаем значение в экземпляре модели:
{
"_id": "5aec65f4853eed12050db4d9",
"email": "ted@example.com",
"password": "$2b$10$qAovc0m0VtmtpLg7CRZmcOXPDNi.2WbPjSFkfxSUqh8Pu5lyN4p7G",
"__v": 0
}