能否在单POST请求中同时发送JSON数据与表单文件完成游戏注册?
问题分析与解决思路
核心问题
- Raw JSON请求的问题:接口强制要求上传文件,用Raw JSON发送时无文件,自然触发文件缺失校验错误;若要同时传递JSON数据和文件,必须使用
multipart/form-data格式请求。 - Form-Data请求的问题:
Cannot read property 'some' of undefined是因为req.body.hours未被正确解析为数组——form-data中传递的JSON格式字段默认是字符串,直接调用数组方法some会报错。 - 代码逻辑Bug:
file.name = photo_${game._id}...中game还未创建,game._id不存在,会导致文件名生成失败;且file.mv是异步操作,当前代码未等待文件上传完成就执行Game.create,可能出现文件未上传成功但游戏已创建的不一致情况。
具体修复步骤
1. 前端请求格式调整
发送multipart/form-data请求时,按以下方式传递字段:
name、description、employees直接作为普通文本字段传递hours转换为JSON字符串(如JSON.stringify(hoursArray))后作为文本字段传递
2. 后端代码修正
const path = require('path'); exports.registerGame = asyncHandler(async (req, res, next) => { // 解析form-data中的JSON字段,添加格式校验 let hours; try { hours = JSON.parse(req.body.hours); } catch (err) { return res.status(400).json({ success: false, error: 'Invalid hours format, must be a valid JSON array' }); } const { name, description, employees } = req.body; // 时间重叠校验逻辑不变 const overlappingHours = hours.some((currentHour, currentIndex) => { return hours.slice(currentIndex + 1).some((nextHour) => { return ( (currentHour.opening <= nextHour.opening && nextHour.opening < currentHour.closing) || (currentHour.opening < nextHour.closing && nextHour.closing <= currentHour.closing) || (nextHour.opening <= currentHour.opening && currentHour.closing <= nextHour.closing) ); }); }); if (overlappingHours) { return res.status(400).json({ success: false, error: 'Range of hours are overlapping', }); } // 文件校验逻辑不变 if (!req.files) { return next(new ErrorResponse(`Please upload a file`, 400)); } const file = req.files.file; if (!file.mimetype.startsWith('image')) { return next(new ErrorResponse(`Please upload an image file`, 400)); } if (file.size > process.env.MAX_FILE_UPLOAD) { return next( new ErrorResponse( `Please upload an image less than ${process.env.MAX_FILE_UPLOAD}`, 400 ) ); } // 先创建游戏记录,获取唯一ID const game = await Game.create({ name, description, employees, hours, photo: '' // 先留空,后续更新 }); // 用游戏ID生成唯一文件名 file.name = `photo_${game._id}${path.parse(file.name).ext}`; // 将异步文件上传转为Promise,确保上传完成后再更新数据 await new Promise((resolve, reject) => { file.mv(`${process.env.FILE_UPLOAD_PATH}/${file.name}`, (err) => { if (err) { reject(err); } else { resolve(); } }); }); // 更新游戏的图片字段并保存 game.photo = file.name; await game.save(); res.status(201).json({ success: true, data: game, }); });
3. 关键修正点说明
- JSON字段解析:对
hours字段做JSON解析并添加异常捕获,避免格式错误导致的程序崩溃。 - 文件名生成时机:先创建游戏获取
_id,再用该ID生成文件名,解决game._id未定义的问题。 - 异步操作同步化:将
file.mv包装为Promise并使用await,确保文件上传完成后再更新游戏记录,保证数据一致性。
内容的提问来源于stack exchange,提问作者tukitooo1998
相关产品推荐
相关产品推荐

