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

React提交商品数据无法触发后端接口,请求无响应问题排查

问题描述

开发电商项目的商品创建页面时,发送空POST请求到后端能收到响应,但填写商品信息提交后请求卡住,无任何响应。


前端代码(Create_Product.jsx)

import React, { useEffect, useState } from "react";
import AdminMenu from "../../components/AdminMenu";
import Layout from "../../components/Layout/Layout";
import axios from "axios";
import { Select } from "antd";
import { toast } from "react-toastify";
const { Option } = Select;
export default function CreateProduct() {
  const [category, setCategory] = useState("");
  const [categories, setCategories] = useState([]);
  const [product, setProduct] = useState({
    name: "",
    description: "",
    price: "",
    photo: "",
    isShipping: "",
    category: "",
    quantity: "",
  });
  useEffect(() => {
    getAllCategories();
  }, []);
  async function getAllCategories() {
    try {
      const { data } = await axios.get(
        process.env.REACT_APP_API + "api/category/get-category"
      );
      if (data.success) setCategories(data.data);
    } catch (e) {
      toast.error("Something went wrong while fetching Categories");
      console.log(e);
    }
  }

  async function handleOnClick(e) {
    e.preventDefault();
    try {
      const response = await axios.post(
        process.env.REACT_APP_API + "api/product/create-product",
       product
      );
      if (response.data.success) {
        toast.success(response.data.message);
      }
    } catch (error) {
      console.log(error);
    }
  }
  // console.log(product);
  function handleOnChange(e) {
    const { name, value } = e.target;
    if (name === "photo") {
      const file = e.target.files[0];
      if (file) {
        setProduct((prevValue) => {
          return { ...prevValue, photo: file };
        });
      }
    } else {
      setProduct((prevValue) => {
        return { ...prevValue, [name]: value };
      });
    }
  }
  return (
    <Layout>
      <div className="container-fluid">
        <div className="row">
          <div className="col-md-3">
            <AdminMenu />
          </div>
          <div className="col-md-9">
            <h2 className="mb-2">Create Product</h2>
            <Select
              showSearch
              showArrow
              placeholder="Select Category"
              className="w-50 "
              onChange={(value) =>
                setProduct((prevValue) => ({
                  ...prevValue,
                  category: value,
                }))
              }
            >
              {categories?.map((item, i) => {
                return (
                  <Option key={i} value={item._id}>
                    {item.name}
                  </Option>
                );
              })}
            </Select>
            <div className="mb-2">
              {product.photo && (
                <div className="text-left">
                  <img
                    src={URL.createObjectURL(product.photo)}
                    className="img img-responsive"
                    height={"200px"}
                    alt="product-photo"
                  />
                </div>
              )}
            </div>
            <div className="mb-2">
              <label className="btn btn-outline-secondary mt-2">
                {" "}
                {product.photo ? product.photo.name : "Upload Photo"}
                <input
                  type="file"
                  name="photo"
                  accept="image/*"
                  hidden
                  onChange={handleOnChange}
                />
              </label>
            </div>
            <div className="mb-2">
              <form>
                <div className="form-group">
                  <label htmlFor="exampleInputEmail1">Product Name</label>
                  <input
                    type="text"
                    className="form-control mb-2 w-50"
                    placeholder="Enter product name"
                    value={product.name}
                    name="name"
                    onChange={handleOnChange}
                  />
                </div>
                <div className="form-group">
                  <label htmlFor="description">Description</label>
                  <textarea
                    type="text"
                    name="description"
                    className="form-control mb-2 w-50"
                    rows={3}
                    placeholder="Description"
                    value={product.description}
                    onChange={handleOnChange}
                  />
                </div>
                <div className="form-group">
                  <label htmlFor="exampleInputEmail1">Product price</label>
                  <input
                    type="text"
                    className="form-control mb-2 w-50"
                    placeholder="Price"
                    value={product.price}
                    name="price"
                    onChange={handleOnChange}
                  />
                </div>
                <div className="form-group">
                  <label htmlFor="exampleInputEmail1">Product quantity</label>
                  <input
                    type="text"
                    className="form-control mb-2 w-50"
                    placeholder="Quantity"
                    value={product.quantity}
                    name="quantity"
                    onChange={handleOnChange}
                  />
                </div>
                <div className="form-group">
                  <label htmlFor="Shipping">Shipping ?</label>
                  <Select
                    placeholder="Is product shipping available"
                    className="w-50 form-control mb-4"
                    onChange={(value) =>
                      setProduct((prevValue) => ({
                        ...prevValue,
                        isShipping: value,
                      }))
                    }
                    name="isShipping"
                  >
                    <Option value={true}>TRUE</Option>
                    <Option value={false}>FALSE</Option>
                  </Select>
                </div>
                <div className="form-group">
                  <button
                    type="submit"
                    className="btn btn-success"
                    onClick={handleOnClick}
                  >
                    Create product
                  </button>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </Layout>
  );
}

后端代码

路由配置

router.post('/create-product',requireSignIn,isAdmin,formidable(),createProductController);

核心控制器代码

import slugify from "slugify";
import Product from "../../model/productModel.js";
import fs from "fs";


export default async function createProductController(req, res) {
  try {
    console.log("I am triggered");
    const { name, description, price, category, isShipping , quantity } = req.fields;
    console.log( name, description, price, category, isShipping , quantity);
    const { photo } = req.files;
    if (!name || !description || !price || !category || !photo || !quantity) {
      return res
        .status(200)
        .json({ success: false, message: "Empty fields found" });
    }

    if (photo.size > 100000) {
      return res
        .status(200)
        .json({ success: false, message: "Photo size limit crossed" });
    }

    const product = new Product({
      ...req.fields,
      slug: slugify(name),
    })

    if (photo) {
      product.photo.data = fs.readFileSync(photo.path);
      product.photo.contentType = photo.type;
    }

    await product.save();

    return res
      .status(200)
      .json({ success: true, message: "Product created successfully" });
  } catch (e) {
    console.log(e);
    return res
      .status(500)
      .json({ success: false, message: "Error in product creation", e });
  }
}

解决方案

问题根源是前端提交数据格式与后端期望不匹配:后端用formidable中间件处理multipart/form-data类型的文件上传请求,但前端直接发送了JSON对象,导致后端无法解析请求,进而卡住。

1. 修改前端提交逻辑,使用FormData发送数据

修改handleOnClick函数,将商品数据包装为FormData对象再提交:

async function handleOnClick(e) {
  e.preventDefault();
  try {
    const formData = new FormData();
    // 遍历product对象,添加所有非空字段和文件
    Object.entries(product).forEach(([key, value]) => {
      if (value) {
        formData.append(key, value);
      }
    });

    const response = await axios.post(
      process.env.REACT_APP_API + "api/product/create-product",
      formData
    );

    if (response.data.success) {
      toast.success(response.data.message);
      // 提交成功后重置表单
      setProduct({
        name: "",
        description: "",
        price: "",
        photo: "",
        isShipping: "",
        category: "",
        quantity: ""
      });
    }
  } catch (error) {
    console.error(error);
    toast.error(error.response?.data?.message || "商品创建失败");
  }
}

2. 可选:优化后端字段类型处理

前端输入的price、quantity是字符串,isShipping是布尔值字符串,后端可做类型转换避免数据库存储异常:

export default async function createProductController(req, res) {
  try {
    console.log("I am triggered");
    const { name, description, price, category, isShipping , quantity } = req.fields;
    console.log( name, description, price, category, isShipping , quantity);
    const { photo } = req.files;
    
    // 类型转换
    const parsedPrice = parseFloat(price);
    const parsedQuantity = parseInt(quantity, 10);
    const parsedIsShipping = isShipping === 'true' ? true : false;

    if (!name || !description || !parsedPrice || !category || !photo || !parsedQuantity) {
      return res
        .status(200)
        .json({ success: false, message: "Empty fields found" });
    }

    if (photo.size > 100000) {
      return res
        .status(200)
        .json({ success: false, message: "Photo size limit crossed" });
    }

    const product = new Product({
      name,
      description,
      price: parsedPrice,
      category,
      isShipping: parsedIsShipping,
      quantity: parsedQuantity,
      slug: slugify(name),
    })

    if (photo) {
      product.photo.data = fs.readFileSync(photo.path);
      product.photo.contentType = photo.type;
    }

    await product.save();

    return res
      .status(200)
      .json({ success: true, message: "Product created successfully" });
  } catch (e) {
    console.log(e);
    return res
      .status(500)
      .json({ success: false, message: "Error in product creation", e });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 22:44:54