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

能否在单POST请求中同时发送JSON数据与表单文件完成游戏注册?

问题分析与解决思路

核心问题

  1. Raw JSON请求的问题:接口强制要求上传文件,用Raw JSON发送时无文件,自然触发文件缺失校验错误;若要同时传递JSON数据和文件,必须使用multipart/form-data格式请求。
  2. Form-Data请求的问题:Cannot read property 'some' of undefined是因为req.body.hours未被正确解析为数组——form-data中传递的JSON格式字段默认是字符串,直接调用数组方法some会报错。
  3. 代码逻辑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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:27:53