You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose子Schema的pre 'save'钩子调用静态encrypt函数报错求助

子Schema的pre save钩子中调用自定义加密函数问题

问题描述

在passwordSchema的pre 'save'钩子中调用自定义encrypt静态函数时,报错this.encrypt不是函数。相关代码如下:

pre钩子代码

passwordSchema.pre('save', async function() {
    if (this.isModified('password')) {
        try {
            this.password = this.encrypt(this.password);
        } catch (error) {
            throw new Error('Error while encrypting the password : ' + error.message);
        }
    }

    if (this.isModified('login')) {
        try {
            this.login = passwordSchema.encrypt(this.login);
        } catch (error) {
            throw new Error('Error while encrypting the login : ' + error.message);
        }
    }
});

encrypt静态函数

passwordSchema.statics.encrypt = function(password) {
    const cipher = crypto.createCipheriv('aes-256-cbc', CRYPTO_AES);

    let encryptedPassword = cipher.update(password, 'utf8', 'hex');

    encryptedPassword += cipher.final('hex');

    return encryptedPassword;
};

passwordSchema定义

const passwordSchema = new mongoose.Schema({
    login: {
        type: String,
        required: [true, "Login required"]
    },

    password: {
        type: String,
        required: [true, "Password required"]
    },

    commentaire: {
        type: String
    }
});

额外说明

该passwordSchema未导出,作为子Schema被collectionSchema使用:

const collectionSchema = new mongoose.Schema(
    {
        [...],

        passwords: [passwordSchema]
    },

    {
        timestamps: true
    }
);

尝试过使用constructor以及类似Password.encrypt的方式,仍无法解决,需找到在pre 'save'钩子中调用该静态函数的正确方法。

解决方案

核心问题

子文档实例的this指向自身,而静态函数挂载在Schema的statics对象上,子Schema未生成独立模型时,直接通过实例或Schema调用会存在指向问题。以下是几种可行方案:

方案一:将encrypt改为实例方法

把加密逻辑定义为Schema的methods,子文档实例可直接通过this.encrypt调用:

// 替换statics.encrypt为methods.encrypt
passwordSchema.methods.encrypt = function(str) {
    const cipher = crypto.createCipheriv('aes-256-cbc', CRYPTO_AES);
    let encryptedStr = cipher.update(str, 'utf8', 'hex');
    encryptedStr += cipher.final('hex');
    return encryptedStr;
};

// 修改后的钩子代码
passwordSchema.pre('save', async function() {
    if (this.isModified('password')) {
        try {
            this.password = this.encrypt(this.password);
        } catch (error) {
            throw new Error('Error while encrypting the password : ' + error.message);
        }
    }

    if (this.isModified('login')) {
        try {
            this.login = this.encrypt(this.login);
        } catch (error) {
            throw new Error('Error while encrypting the login : ' + error.message);
        }
    }
});

方案二:提取为独立工具函数

将加密逻辑抽成单独函数,直接在钩子中调用,同时可按需挂载到statics上:

// 独立加密函数
function encryptStr(str) {
    const cipher = crypto.createCipheriv('aes-256-cbc', CRYPTO_AES);
    let encryptedStr = cipher.update(str, 'utf8', 'hex');
    encryptedStr += cipher.final('hex');
    return encryptedStr;
}

// 钩子中调用
passwordSchema.pre('save', async function() {
    if (this.isModified('password')) {
        try {
            this.password = encryptStr(this.password);
        } catch (error) {
            throw new Error('Error while encrypting the password : ' + error.message);
        }
    }

    if (this.isModified('login')) {
        try {
            this.login = encryptStr(this.login);
        } catch (error) {
            throw new Error('Error while encrypting the login : ' + error.message);
        }
    }
});

// 若后续需要模型级调用,仍可挂载statics
passwordSchema.statics.encrypt = encryptStr;

方案三:通过父模型间接调用(不推荐)

如果坚持使用静态函数,可通过父模型的Schema路径找到子Schema的statics,但耦合性高:

passwordSchema.pre('save', async function() {
    // 通过父文档的构造函数找到子Schema的静态方法
    const encryptFn = this.parent().constructor.schema.path('passwords').schema.statics.encrypt;
    
    if (this.isModified('password')) {
        try {
            this.password = encryptFn(this.password);
        } catch (error) {
            throw new Error('Error while encrypting the password : ' + error.message);
        }
    }

    if (this.isModified('login')) {
        try {
            this.login = encryptFn(this.login);
        } catch (error) {
            throw new Error('Error while encrypting the login : ' + error.message);
        }
    }
});

内容的提问来源于stack exchange,提问作者user14766108

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:54:54