Mongoose pre-validate钩子映射对象数组为字符串数组时丢失值怎么办?
问题
尝试用请求体中的数据创建新记录,请求体数据如下:
{ extras: [{value: "string"}, {value: "string"}, {value: "string"}], skills: [{value: "string"}, {value: "string"}] }
这些属性是对象数组。POST请求处理器代码如下:
const record = new Record(req.body); try { const newRecord = await record.save(); res.status(200).json(newRecord); } catch (err) { res.status(400).json({ message: err.message }); }
但Mongoose Schema要求这些属性为字符串数组,于是添加了pre-validate钩子来在保存前将对象值转为字符串:
const recordSchema = new Schema( { extras: [String], skills: [String] }, { timestamps: true } ) recordSchema.pre('validate', function (next) { console.log(this); this.extras = this.extras.map(v => v.value); this.skills = this.skills.map(v => v.value); return next(); });
但钩子中打印this时,发现值丢失,输出:
{ extras: [], skills: [] }
请问如何确保将对象正确映射为字符串后再保存记录?
解决方案
问题核心是Mongoose在实例化时会提前根据Schema做类型校验与转换:因为你定义的extras和skills是字符串数组,当用new Record(req.body)创建实例时,Mongoose会自动把不符合格式的对象数组清空为空数组,导致pre-validate钩子拿到的已经是处理后的数据。
以下是三种可靠的解决办法:
方法1:实例化前手动转换请求体数据
直接在创建Record实例前,先处理请求体里的对象数组,绕过Mongoose的自动类型校验:
// 处理请求体,将对象数组转为字符串数组 const processedBody = { ...req.body, extras: req.body.extras?.map(item => item.value) || [], skills: req.body.skills?.map(item => item.value) || [] }; const record = new Record(processedBody); try { const newRecord = await record.save(); res.status(200).json(newRecord); } catch (err) { res.status(400).json({ message: err.message }); }
方法2:用自定义字段转换器处理
给Schema字段添加set转换器,在数据被设置到实例时自动完成转换,这是最简洁的Schema层面解决方案:
// 定义通用转换函数:将对象数组转为字符串数组 const convertObjectArrayToStringArray = function(value) { if (!Array.isArray(value)) return value; return value.map(item => item?.value || String(item)); }; const recordSchema = new Schema( { extras: { type: [String], set: convertObjectArrayToStringArray }, skills: { type: [String], set: convertObjectArrayToStringArray } }, { timestamps: true } );
set函数会在数据赋值到实例时自动触发,转换后的数据会自动符合Schema的字符串数组要求,无需额外钩子。
方法3:修改Schema为混合类型后再转换
先将字段定义为混合类型(Mixed)避免Mongoose自动清空数据,再在pre-validate钩子中完成转换:
const recordSchema = new Schema( { extras: Schema.Types.Mixed, skills: Schema.Types.Mixed }, { timestamps: true } ) recordSchema.pre('validate', function (next) { // 转换对象数组为字符串数组,兼容非对象的异常情况 this.extras = Array.isArray(this.extras) ? this.extras.map(item => typeof item === 'object' ? item.value : String(item)) : []; this.skills = Array.isArray(this.skills) ? this.skills.map(item => typeof item === 'object' ? item.value : String(item)) : []; next(); });
内容的提问来源于stack exchange,提问作者sansalgo
相关产品推荐
相关产品推荐

