Mongoose pre-validate中间件中categories数组为空问题求助
问题:Mongoose pre-validate中间件中categories数组为空的排查与解决
我是JS、Mongoose和Express的新手,想在pre-validate中间件里处理categories数组——Schema要求数组是ObjectID类型,但为了用户方便(分类名称唯一),允许传入分类名称而非ObjectID。但发现调用Project.create前的路由里categories数组有预期的字符串值,到pre-validate中间件里就变成空数组了,其他字段都正常传递。
相关代码与日志
路由接收multipart form-data的代码
router.post('/', uploadFiles.fields([{ name: 'gszfile', maxCount: 1 }, { name: 'imagefile', maxCount: 1 }]), async (req, res) => { try { const { projectdata, versiondata } = req.body; const projectData = JSON.parse(projectdata); console.log(`projectdata: ${JSON.stringify(projectData)}`) console.log(`projectData.categories: ${projectData.categories}`) //此处categories不为空 const newProject = await Project.create(projectData);
Schema中的pre-validate中间件代码
project_schema.pre('validate', async function(next) { const categories = this.categories; console.log(`this: ${JSON.stringify(this)}`) //此处categories为空 console.log(`categories: ${categories}`) });
路由中的控制台日志
projectdata: {"title":"activator","shortdescription":"A stochastic model of the activator-substrate process","gszfile":"activator.gsz","author":"","description":"","howitworks":"","thingstonotice":"","thingstotry":"","extendingthemodel":"","relatedmodels":"","creditsandreferences":"","createdAt":"2009-04-20T00:00:00.000+01:00","updatedAt":"2009-04-20T00:00:00.000+01:00","categories":["Intern","FormalSystems"],"access":{"public":true,"readAccess":[],"writeAccess":[]},"imagefile":"activator.png"} projectData.categories: Intern,FormalSystems
中间件中的控制台日志
this: {"title":"activator","shortdescription":"A stochastic model of the activator-substrate process","description":"","howitworks":"","thingstonotice":"","thingstotry":"","extendingthemodel":"","relatedmodels":"","creditsandreferences":"","categories":[],"tags":[],"imagefile":"activator.png","versions":[],"access":{"public":true,"readAccess":[],"writeAccess":[]},"_id":"65e98a15a58ee683b2bfa0e8","createdAt":"2009-04-19T23:00:00.000Z","updatedAt":"2009-04-19T23:00:00.000Z"} categories:
原因分析
这种情况核心原因是Mongoose的Schema类型校验逻辑:你的categories字段被定义为[mongoose.Types.ObjectId]类型,但传入的是字符串数组。Mongoose在实例化文档时,会先执行类型转换与基础校验,无法转换为ObjectID的字符串会被直接过滤,而pre-validate钩子是在这一步之后才触发的,所以此时数组已经被清空。
解决办法
方法1:路由层提前转换(推荐)
在调用Project.create之前,手动将分类名称转换为对应的ObjectID,确保传入的数组符合Schema类型要求:
// 假设存在Category模型,用于根据名称查询分类ID const categoryIds = await Promise.all( projectData.categories.map(async name => { const category = await Category.findOne({ name }); if (!category) { throw new Error(`分类 ${name} 不存在`); // 处理分类不存在的异常情况 } return category._id; }) ); projectData.categories = categoryIds; // 再执行创建操作 const newProject = await Project.create(projectData);
方法2:改用pre-save钩子处理
如果希望在模型层完成转换,可以改用pre('save')钩子(它在类型转换后、文档保存前触发),但需要确保转换后的元素都是有效ObjectID:
project_schema.pre('save', async function(next) { // 仅在新增文档且categories有值时处理 if (this.isNew && this.categories.length > 0) { const categoryIds = await Promise.all( this.categories.map(async name => { const category = await Category.findOne({ name }); return category?._id; }) ).then(ids => ids.filter(id => id)); // 过滤掉不存在的分类ID this.categories = categoryIds; } next(); });
方法3:临时放宽Schema类型(不推荐)
暂时将categories字段类型改为[mongoose.Schema.Types.Mixed],让Mongoose不自动过滤字符串,待中间件转换完成后再改回ObjectID数组。这种方式会丢失Schema的类型约束,仅适合临时调试。
内容的提问来源于stack exchange,提问作者Dodo
相关产品推荐
相关产品推荐

