使用ReactJS将对象数组发布到REST API

使用ReactJS将对象数组发布到REST API,reactjs,mongodb,api,rest,express,Reactjs,Mongodb,Api,Rest,Express,我正在做一个项目,我需要在我的API上发布一个新的课程。我用POSTMAN和API对此进行了测试,但当我尝试使用react fetch发布数据时,数据已损坏。当发送像dishName这样的单个字符串时:“pizza”工作得很好,并且显示在数据库中,我无法发送对象数组。我尝试了很多方法,比如: [ { "quantity": 1, "unit": ""

我正在做一个项目,我需要在我的API上发布一个新的课程。我用POSTMAN和API对此进行了测试,但当我尝试使用react fetch发布数据时,数据已损坏。当发送像dishName这样的单个字符串时:“pizza”工作得很好,并且显示在数据库中,我无法发送对象数组。我尝试了很多方法,比如:

 [
            {
                "quantity": 1,
                "unit": "",
                "description": "egg"
            },
            {
                "quantity": 0.5,
                "unit": "cup",
                "description": "mozzarella shredded"
            }
]
或:

还有很多很多组合,但我失败了

下面是我的React类,它负责将数据发送到API:

import React, { useState } from "react";
import { useHistory } from "react-router-dom";

export default function Login() {
  const [dishName, setdishName] = useState("");
  const [category, setcategory] = useState("");
  const [author, setauthor] = useState("");
  const [ingredients, setingredients] = useState([]);
  const [cookingTime, setcookingTime] = useState("");
  const [sourceUrl, setsourceUrl] = useState("");
  const [imageUrl, setimageUrl] = useState("");
  const [isPublished, setisPublished] = useState("true");
  const [price, setprice] = useState("");
  const [tags, settags] = useState([]);
  const history = useHistory();

  async function login() {
    let item = {
      dishName,
      category,
      author,
      ingredients,
      cookingTime,
      sourceUrl,
      imageUrl,
      isPublished,
      price,
      tags,
    };

    await fetch("http://localhost:1234/api/courses", {
      method: "POST",
      body: JSON.stringify(item),
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    });

    history.push("/addCourse");
    //   window.location.reload();
  }

  return (
    <div className="col-sm-6" style={{ textAlign: "center" }}>
      <h1 className="bigBar">Create new recipe</h1>
      <input
        type="text"
        onChange={e => setdishName(e.target.value)}
        className="form-control"
        placeholder="dishName"
      />
      <br />

      <input
        type="text"
        onChange={e => setcategory(e.target.value)}
        className="form-control"
        placeholder="cathegory"
      />
      <br />

      <input
        type="text"
        onChange={e => setauthor(e.target.value)}
        className="form-control"
        placeholder="author"
      />
      <br />

      <input
        type="text"
        onChange={e => setingredients(e.target.value)}
        className="form-control"
        placeholder='{"quantity": int, "unit": "", "description": ""}'
      />
      <br />

      <input
        type="text"
        onChange={e => setcookingTime(e.target.value)}
        className="form-control"
        placeholder="cooking time"
      />
      <br />

      <input
        type="text"
        onChange={e => setsourceUrl(e.target.value)}
        className="form-control"
        placeholder="source url"
      />
      <br />

      <input
        type="text"
        onChange={e => setimageUrl(e.target.value)}
        className="form-control"
        placeholder="image url"
      />
      <br />

      <input
        type="text"
        onChange={e => setisPublished(e.target.value)}
        className="form-control"
        placeholder="publish status (dafault: true)"
      />
      <br />

      <input
        type="text"
        onChange={e => setprice(e.target.value)}
        className="form-control"
        placeholder="price"
      />
      <br />

      <input
        type="text"
        onChange={e => settags(e.target.value)}
        className="form-control"
        placeholder="tags"
      />
      <br />

      <button onClick={login} className="btn btn-primary">
        Sign up
      </button>
    </div>
  );
}
以及新课程的模式:

router.post("/", async (req, res) => {
  const { error } = validateCourse(req.body);

  if (error)
    //   400 Bad request
    return res.status(400).send(error.details[0].message);

  let course = new Course(
    _.pick(req.body, [
      `dishName`,
      `category`,
      `author`,
      `ingredients`,
      `cookingTime`,
      `sourceUrl`,
      `imageUrl`,
      `isPublished`,
      `price`,
      //  `tags`,
    ])
  );
const mongoose = require(`mongoose`);
const Joi = require(`joi`);

function validateCourse(body) {
  const schema = Joi.object({
    dishName: Joi.string().min(3).required(),
    category: Joi.string().min(3).required(),
    author: Joi.string().min(3).required(),
    ingredients: Joi.required(),
    cookingTime: Joi.number().min(0).required(),
    sourceUrl: Joi.string().required(),
    imageUrl: Joi.string().required(),
    isPublished: Joi.boolean().required(),
    price: Joi.number().min(0).required(),
    tags: Joi.required(),
    date: Joi.allow(),
  });
  return schema.validate(body);
}

const Course = mongoose.model(
  `Course`,
  new mongoose.Schema({
    dishName: { type: String, required: true, minLength: 3, maxLength: 255 },
    category: {
      type: String,
      required: true,
      lowercase: true,
      trim: true,
      dishType: [
        "carrot",
        "broccoli",
        "asparagus",
        "cauliflower",
        "corn",
        "cucumber",
        "green pepper",
        "lettuce",
        "mushrooms",
        "onion",
        "potato",
        "pumpkin",
        "red pepper",
        "tomato",
        "beetroot",
        "brussel sprouts",
        "peas",
        "zucchini",
        "radish",
        "sweet potato",
        "artichoke",
        "leek",
        "cabbage",
        "celery",
        "chili",
        "garlic",
        "basil",
        "coriander",
        "parsley",
        "dill",
        "rosemary",
        "oregano",
        "cinnamon",
        "saffron",
        "green bean",
        "bean",
        "chickpea",
        "lentil",
        "apple",
        "apricot",
        "avocado",
        "banana",
        "blackberry",
        "blackcurrant",
        "blueberry",
        "boysenberry",
        "cherry",
        "coconut",
        "fig",
        "grape",
        "grapefruit",
        "kiwifruit",
        "lemon",
        "lime",
        "lychee",
        "mandarin",
        "mango",
        "melon",
        "nectarine",
        "orange",
        "papaya",
        "passion fruit",
        "peach",
        "pear",
        "pineapple",
        "plum",
        "pomegranate",
        "quince",
        "raspberry",
        "strawberry",
        "watermelon",
        "salad",
        "pizza",
        "pasta",
        "popcorn",
        "lobster",
        "steak",
        "bbq",
        "pudding",
        "hamburger",
        "pie",
        "cake",
        "sausage",
        "Tacos",
        "Kebab",
        "poutine",
        "seafood",
        "chips",
        "fries",
        "masala",
        "paella",
        "som tam",
        "chicken",
        "toast",
        "marzipan",
        "tofu",
        "Ketchup",
        "hummus",
        "chili",
        "maple syrup",
        "parma ham",
        "fajitas",
        "champ",
        "lasagna",
        "poke",
        "chocolate",
        "croissant",
        "arepas",
        "bunny chow",
        "pierogi",
        "donuts",
        "rendang",
        "sushi",
        "ice cream",
        "duck",
        "curry",
        "beef",
        "goat",
        "lamb",
        "turkey",
        "pork",
        "fish",
        "crab",
        "bacon",
        "ham",
        "pepperoni",
        "salami",
        "ribs",
        "other",
      ],
    },
    author: String,
    ingredients: [
      {
        quantity: Number,
        unit: String,
        description: String,
      },
    ],
    cookingTime: { type: Number, required: true },
    sourceUrl: String,
    imageUrl: String,
    date: { type: Date, default: Date.now },
    isPublished: { type: Boolean, default: true },
    price: {
      type: Number,
      required: function () {
        return this.isPublished;
      },
    },
    tags: {
      type: Array,
      validate: {
        validator: function (v) {
          return v && v.length > 0;
        },
        message: `A course should have at least one tag.`,
      },
    },
  })
);

exports.Course = Course;
exports.validateCourse = validateCourse;

您正在将
成分
状态设置为字符串,因此基本上是将字符串“字符串化”,这将导致
JSON语法错误
。如果要以这种方式发送数组,必须指定数组括号
[
]
,以使其成为有效数组

要解决这个问题,只需改变:


      <input
        type="text"
        onChange={e => setingredients(e.target.value)}
        className="form-control"
        placeholder='{"quantity": int, "unit": "", "description": ""}'
      />

setComponents(例如target.value)}
className=“表单控件”
占位符='{“数量”:int,“单位”:“说明”:“}”
/>
您需要将其更改为:


      <input
        type="text"
        //parse the string input
        onChange={e => setingredients(JSON.parse(e.target.value))}
        className="form-control"
        //notice the array bracket '[' and ']'
        placeholder='[{"quantity": int, "unit": "", "description": ""}, {"quantity": int, "unit": "", "description": ""}]'
      />

setComponents(JSON.parse(e.target.value))}
className=“表单控件”
//请注意数组括号“[”和“]”
占位符='[{“数量”:int,“单位”:“说明”:“},{“数量”:int,“单位”:“说明”:“}]'
/>

您好,谢谢您的回复。遗憾的是,你们告诉我的(在输入字段中添加[])不允许我毫无错误地发布。我仍然得到:Uncaught SyntaxError:JSON输入意外结束,无法加载资源:当我尝试使用[]发布成分时,服务器响应状态为500(内部服务器错误),但没有添加SetComponents(JSON.parse(e.target.value))}我只得到500个错误。但是,您是否向该字段输入了有效的JSON?试试这个
[{“数量”:“1”,“单位”:“描述”:“鸡蛋”}]
你好,我试着按照你告诉我的那样做,但我收到了一个错误:
“课程验证失败:成分:转换为嵌入值失败”
'[{“数量”:“1\”,“单位”:“鸡蛋”,“描述”:“鸡蛋”}'
“(类型字符串)路径\“成分”

      <input
        type="text"
        //parse the string input
        onChange={e => setingredients(JSON.parse(e.target.value))}
        className="form-control"
        //notice the array bracket '[' and ']'
        placeholder='[{"quantity": int, "unit": "", "description": ""}, {"quantity": int, "unit": "", "description": ""}]'
      />