我的方法错了吗?我对 NodeJS、MongoDB、Joi、Bcrypt 感到困惑

Is my approach wrong? I'm confused with NodeJS, MongoDB, Joi, Bcrypt

我在使用 NodeJS(ExpressJS)、MongoDB、Joi 验证模块和 Bcrypt 时遇到一些问题。

我有用户 API 路由器和用户模型。 我的用户模型是这样的:

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        trim: true,
        minlength: [3, 'Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.'],
        maxlength: [50, 'Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.'],
        validate: {
            validator: function(v) {
                return /^[A-Za-zıİüÜğĞşŞçÇöÖ ]+$/.test(v);
            },
            message: 'Uygun Formatta İsim Giriniz. (Geçerli Karakterler: a-z, A-Z, boşluk karakteri.)'
        },
        match: [/^[A-Za-zıİüÜğĞşŞçÇöÖ ]+$/, 'Uygun Formatta İsim Giriniz. (Geçerli Karakterler: a-z, A-Z, boşluk karakteri.)']
    },
    lastname: {
        type: String,
        trim: true,
        minlength: [3, 'Soyadınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.'],
        maxlength: [50, 'Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.'],
        validate: {
            validator: function(v) {
                return /^[A-Za-zıİüÜğĞşŞçÇöÖ ]+$/.test(v);
            },
            message: 'Uygun Formatta Soyisim Giriniz. (Geçerli Karakterler: a-z, A-Z, boşluk karakteri.)'
        },
        match: [/^[A-Za-zıİüÜğĞşŞçÇöÖ ]+$/, 'Uygun Formatta Soyisim Giriniz. (Geçerli Karakterler: a-z, A-Z, boşluk karakteri.)']
    },
    username: {
        type: String,
        trim: true,
        index: true,
        unique: true,
        minlength: [3, 'Kullanıcı Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 30 Adet Karakterden Oluşabilir.'],
        maxlength: [20, 'Kullanıcı Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 30 Adet Karakterden Oluşabilir.'],
        validate: {
            validator: function(v) {
                let re = /^\w+$/;
                return re.test(v);
            },
            message: 'Uygun Formatta Kullanıcı Adı Giriniz. (Geçerli Karakterler: a-z, A-Z, _.)'
        },
        match: [/^\w+$/, 'Uygun Formatta Kullanıcı Adı Giriniz. (Geçerli Karakterler: a-z, A-Z, _.)']
    },
    email: {
        type: String,
        trim: true,
        index: true,
        unique: true,
        validate: {
            validator: function(v) {
                let re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
                return re.test(v);
            },
        message: 'Uygun Formatta E-Posta Adresi Giriniz.'
        },
        match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Uygun Formatta E-Posta Adresi Giriniz.']
    },
    password: {
        type: String,
        trim: true,
        required: [true, 'Şifrenizi Girmeniz Gerekmektedir.'],
        minlength: 5,
        maxlength: 1024
    },
    elo: {
        type: Number,
        min: 0,
        required: false,
        default: 0
    },
    rank: {
        type: Number,
        min: -1,
        required: false,
        default: 0
    },
    registerDate: {
        type: Date,
        default: Date.now
    },
    isAdmin: {
        type: Boolean,
        required: true,
        default: false
    }
});

这是 Joi 验证模式:

const Schema = {
    name: Joi.string().min(3).max(50).regex(/^[A-Za-zıİüÜğĞşŞçÇöÖ ]+$/),
    lastname: Joi.string().min(3).max(50).regex(/^[A-Za-zıİüÜğĞşŞçÇöÖ ]+$/),
    username: Joi.string().required().min(3).max(25).regex(/^\w+$/),
    email: Joi.string().required().min(3).email(),
    password: Joi.string().required().min(5).max(255),
    elo: Joi.number().default(0).min(0),
    rank: Joi.number().default(0).min(-1),
    isAdmin: Joi.boolean().default(false)
};

这是示例用户:

_id:5b47aae43cf7710e7047c033
elo:0
rank:0
isAdmin:true
name:"Furkan"
lastname:"Taştan"
username:"praaven"
email:"furkantastan@superonline.com"
password:"bxKTxmQnV5mnGrCccpQsY.A8j5ACCdmCHIihDdW0NXmcOcRCcwwPq"
registerDate:2018-07-12 22:24:20.778
__v:0

我对更新 API 路由器感到困惑。

router.put('/:id', auth, async (req, res) => {
    try {
        const errorId = validateUserId(req.params.id);
        if(errorId.error) return res.status(400).send({'Hata': errorId.error.details[0].message});
        const { error } = validateUser(req.body);
        if(error) return res.status(400).send({'Hata': error.details[0].message});
        const salt = await bcyript.genSalt(10);
        const user = await User.findByIdAndUpdate(req.params.id, {
            name: req.body.name,
            lastname: req.body.lastname,
            username: req.body.username,
            email: req.body.email,
            password: await bcyript.hash(req.body.password, salt),
            elo: req.body.elo,
            rank: req.body.rank,
            isAdmin: req.body.isAdmin
        }, { new: true, runValidators: true, context: 'query' });
        if(!user) return res.status(404).send({'Hata': 'Belirtilen ID\'ye Sahip Kullanıcı Bulunamadı.'});
        res.status(200).send(_.pick(user, ['_id', 'name', 'lastname', 'username', 'email', 'elo', 'rank', 'registerDate', 'isAdmin']));
    } catch(e) {
        res.status(400).send({ 'Hata': e.message.replace(/(Validation failed|email|username|phone|team)/gi, v => arrayMap[v]) });
    }
});

问题是,为什么我需要每个更新用户的密码?我不想要用户的密码,我想在可选的情况下使用它。我认为,我的 Update Router 是错误的,我是 NodeJS 的新手 RESTful API 编码,你能帮我弄清楚它的真实性吗?

谢谢,祝你有个愉快的一天!

您已经在此处 Schema 级别验证密码字段:

password: {
        type: String,
        trim: true,
        required: [true, 'Şifrenizi Girmeniz Gerekmektedir.'],
        minlength: 5,
        maxlength: 1024
    },

因此无需在 JOI 架构中使用 required() 重新验证它。

由于您正在使用 Mongoose,因此您可以在 userSchema 中使用 pre 函数。像这样:

/**
 * Hash password with blowfish algorithm (bcrypt) before saving it in to the database
 */
userSchema.pre('save', function(next) {
    var user = this;

    // only hash the password if it has been modified
    if (!user.isModified('password'))
        return next();

    // password will be hashed only if it has changed
    user.password = bcrypt.hash(user.password, bcrypt.genSalt(10));
    next();
});

这真的是因为 required() 您在 JOI 架构中提出的。

作为解决方案,您有多种方法可以做到这一点。您可能希望公开另一个 API 来专门处理密码和用户名的细微差别。通过这种方式,您最终创建了两个单独的 JOI 模式来处理两种不同的情况——在真正需要时更改密码,以及在更新详细信息时(我不确定在更新详细信息时来回移动密码是否很好想法)。

附录:即便如此,我发现您在将数据从请求正文传递到数据库模型时实际上并未在此处对数据进行任何转换(密码除外)- 我不确定您为什么要这么做对于这种情况,希望验证此数据两次(请记住,您在更新时也打开了 runValidators 开关)。