Node React,Action到达后端但不保存产品

Posted

技术标签:

【中文标题】Node React,Action到达后端但不保存产品【英文标题】:Node React, Action reaches the backend but does not save the product 【发布时间】:2020-05-13 05:09:15 【问题描述】:

所以,我创建了模型、路由、控制器,从代码来看一切都很好。我还为前端创建了减速器、动作和组件。当我尝试创建产品时,它会触发操作并且似乎正在成功调度,但在 redux devtools 中,我可以看到我在产品数组中获得的项目为 NULL。此外,当我检查我的数据库时,产品没有得到保存。奇怪的是,我可以在位于后端的图像文件夹中看到我的图像。我不知道我是否正确设置了动作、组件或控制器,甚至可能是路由。如果有人可以对此进行调查,我会很高兴,也许我的实施是错误的。我将链接我的 Github 并在下面粘贴一些代码。

顺便说一句,我能够注册、登录和获取用户,所以它与此功能有关。 如果您认为我的某些方法有误,我也很高兴更改我的应用程序中的内容,我是这个 Stack 的新手

Github 链接:https://github.com/tigerabrodi/eBuy

server.js(后端)

const express = require('express');
const connectDB = require('./config/db');
const uuidv4 = require("uuid/v4")
const colors = require("colors");
const morgan = require("morgan");
const multer = require("multer");
const path = require("path");
const app = express();
const PORT = process.env.PORT || 9045;

// Importing Routes
const authRoutes = require("./routes/auth");
const productRoutes = require("./routes/products");

// Connect Database
connectDB();

// Configuring Multer storage
const fileStorage = multer.diskStorage(
  destination: (req, file, cb) => 
    cb(null, 'images');
  ,
  filename: (req, file, cb) => 
    // I used a regex to trim the name from spaces
    cb(null, uuidv4() + "_" + file.originalname.replace(/\s/g, ''));
  
);

const fileFilter = (req, file, cb) => 
  if (
    file.mimetype === 'image/png' ||
    file.mimetype === 'image/jpg' ||
    file.mimetype === 'image/jpeg'
  ) 
    cb(null, true);
   else 
    cb(null, false);
  
;


// Init Middleware
app.use(express.json( extended: false ));

// Use Multer
app.use(
  multer( storage: fileStorage, fileFilter: fileFilter ).single('image')
);

// Serving static for images folder
app.use('/images', express.static(path.join(__dirname, 'images')));


// Dev logging middleware
if (process.env.NODE_ENV === "development") 
  app.use(morgan("dev"));


// Define Routes
app.use('/auth', authRoutes);
app.use('/products', productRoutes);


// Serve static assets in production
if (process.env.NODE_ENV === 'production') 
  // Set static folder
  app.use(express.static('client/build'));

  app.get('*', (req, res) => 
    res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
  );




app.listen(PORT, () => console.log(`Server started on port $PORT`.cyan.underline.bold));

产品路线(后端)

const express = require("express");
const auth = require("../middleware/auth");
const check = require("express-validator");
const productController = require("../controllers/products");
const router = express.Router();


// Create Product
router.post("/", [
    auth,
    [
        check("title", "Title is required").notEmpty(),
        check("description", "Description is required").notEmpty(),
        check("image", "Image is required").notEmpty(),
        check("price", "Price is required").isNumeric()
    ]
], productController.createProduct);

module.exports = router;

产品模型(后端)

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const ProductSchema = new Schema(
    user: 
        type: Schema.Types.ObjectId,
        ref: "User"
    ,
    title: 
        type: String,
        required: true
    ,
    description: 
        type: String,
        required: true
    ,
    price: 
        type: Number,
        required: true
    ,
    image: 
        type: String,
        required: true
    ,
    date: 
        type: Date,
        default: Date.now
    ,

,

    toJSON: virtuals: true
)

ProductSchema.virtual("question", 
    ref: "Question",
    localField: "_id",
    foreignField: "product"
);

module.exports = mongoose.model("Product", ProductSchema);

产品控制器(后端)

const validationResult = require("express-validator");
const User = require("../models/User");
const Product = require("../models/Product");


// @route    POST /products
// @desc     Add Product
// @access   Private
exports.createProduct = async (req, res, next) => 
    const errors = validationResult(req);
    if (!errors.isEmpty()) 
      return res.status(400).json( errors: errors.array() );
    

    try 
      const title, description, price = req.body;

      if (!req.file) 
        return res.status(400).json(msg: "Please upload an Image!")
      

      const image = "/" + req.file.path.replace("\\" ,"/");

      const newProduct = new Product(
        user: req.user.id,
        title,
        description,
        price,
        image
      );

      const product = await newProduct.save();
      res.status(201).json(product);
      console.log(product);
      next();
     catch (err) 
      console.error(err.message);
      res.status(500).send("Server Error");
      next(err);
    

产品减速器(前端)

import ProductActionTypes from "./product.types";

const initialState = 
    products: [],
    totalProducts: null,
    product: null,
    loading: true,
    error: 


const productReducer = (state = initialState, action) => 
    const payload, type = action;
    switch(type) 
        case ProductActionTypes.ADD_PRODUCT:
            return 
                ...state,
                products: [payload, ...state.products],
                loading: false
            
            case ProductActionTypes.PRODUCT_ERROR: 
            return 
                ...state,
                error: payload,
                loading: false
            
        default:
            return state;
    


export default productReducer

产品操作(前端)

import ProductActionTypes from "./product.types"
import setAlert from "../alert/alert.actions"
import axios from "axios"



export const addProduct = (productData, history) => async dispatch => 
    const config = 
        headers: 
            "Content-Type": "multipart/form-data"
        
    ;
    const formData = new FormData();
    formData.append("title", productData.title)
    formData.append("description", productData.description)
    formData.append("price", productData.price)
    formData.append("image", productData.image)
    try 
        const res = axios.post("/products/", formData, config);
        dispatch(
            type: ProductActionTypes.ADD_PRODUCT,
            payload: res.data
        )
        history.push("/dashboard");
        dispatch(setAlert("Product created successfully", "success"))
     catch (err) 
        const errors = err.response.data.errors;
        if (errors) 
          errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
        
        dispatch(
        type: ProductActionTypes.PRODUCT_ERROR,
        payload: msg: err.response.statusText, status: err.response.status
        )
    


产品类型(前端)

export const ProductActionTypes = 
    ADD_PRODUCT: "ADD_PRODUCT",
    PRODUCT_ERROR: "PRODUCT_ERROR"

产品组件(前端)

import React, Fragment, useState from 'react';
import withRouter from "react-router-dom";
import connect from "react-redux";
import addProduct from '../../redux/product/product.actions';

const CreateProduct = (history, addProduct) => 
    const [formData,
        setFormData] = useState(name: "", description: "", price: 10, image: "");
    const [showImage, setShowImage] = useState(false);
    const [imageName, setImageName] = useState("")

    const onSubmit = e => 
        e.preventDefault();
        addProduct(formData, history);
    

    const onChangeImage = e => 
        setFormData(...formData, image: e.target.files[0]);
        setShowImage(true);
        setImageName(e.target.files[0].name)
    

    const onChange = e => setFormData(
        ...formData,
        [e.target.name]: e.target.value
    );

    const name, description, price = formData;
    return (
        <Fragment>
            <div className="container">
                <div className="row">
                    <div className="col text-info font-weight-bold m-2">
                    *- All Fields Requried
                        <form onSubmit=e => onSubmit(e)>
                        <div className="form-group m-2">
                        <label htmlFor="name">Name</label>
                        <input type="text" placeholder="Enter Products Name" name="name" value=name onChange=e => onChange(e) className="form-control" required/>
                        </div>
                        <div className="form-group m-2">
                        <label htmlFor="price">Price</label>
                        <input type="number" name="price" placeholder="Enter Products Price" value=price onChange=e => onChange(e)  className="form-control" required/>
                        </div>
                        <div class="custom-file m-2">
                        <input type="file"  onChange=e => onChangeImage(e)  class="custom-file-input bg-info" required/>
                        <label class="custom-file-label">showImage ? imageName : "Upload Image"</label>
                      </div>
                        <div className="form-group m-2">
                        <label htmlFor="title">Description</label>
                        <textarea name="description" onChange=e => onChange(e) placeholder="Enter Products description" value=description className="form-control" required/>
                        </div>
                        <input type="submit" value="Add Product" className="btn btn-block btn-info"/>
                        </form>
                    </div>
                </div>
            </div>

        </Fragment>
    );


export default connect(null, addProduct)(withRouter(CreateProduct));

【问题讨论】:

在产品操作addProduct中,这里需要添加awaitconst res = axios.post("/products/", formData, config);可以添加重试吗? 是的,我做到了,现在我正在努力上传图片,我在图片文件夹中看到图片,但它仍然说需要图片,我将如何优化我的控制器? 我需要以某种方式访问​​控制器中的图像:( “需要图片”怎么说?你能把这个确切的错误添加到问题中吗? 现在我正在调试一下,因为它说名字是必需的,我的名字和标题出错了,但我现在需要去,我稍后会回来更新这个问题,我希望我应该能够解决这个问题 【参考方案1】:

没什么好说的,正如苏莱曼所说,我忘了等待。其他事情是删除标题,因为 formData 自己会这样做,并且可以通过 req.file.path 访问图像。我将在下面发布我的控制器和操作,供人们稍后参考。

产品负责人

// @route    POST /products
// @desc     Add Product
// @access   Private
exports.createProduct = async (req, res, next) => 
    const errors = validationResult(req);
    if (!errors.isEmpty()) 
      return res.status(400).json( errors: errors.array() );
    

    try 
      console.log(req.body);
      const name, description, price = req.body;

      if (!req.file) 
        return res.status(400).json(msg: "Please upload an Image!")
      

      const image = "/" + req.file.path.replace("\\" ,"/");

      const newProduct = new Product(
        user: req.user.id,
        name,
        description,
        price,
        image
      );

      const product = await newProduct.save();
      res.status(201).json(product);
      console.log(product);
      next();
     catch (err) 
      console.error(err.message);
      res.status(500).send("Server Error");
      next(err);
    

产品操作

export const addProduct = (productData, history) => async dispatch => 
    const formData = new FormData();
    formData.append("name", productData.name);
    formData.append("description", productData.description);
    formData.append("price", productData.price);
    formData.append("image", productData.image);
    try 
        const res = await axios.post("/products", formData);
        dispatch(
            type: ProductActionTypes.ADD_PRODUCT,
            payload: res.data
        );
        history.push("/dashboard");
        dispatch(setAlert("Product created successfully", "success"))
     catch (err) 
        const errors = err.response.data.errors;
        if (errors) 
          errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
        
        dispatch(
        type: ProductActionTypes.PRODUCT_ERROR,
        payload: msg: err.response.statusText, status: err.response.status
        )
    

【讨论】:

以上是关于Node React,Action到达后端但不保存产品的主要内容,如果未能解决你的问题,请参考以下文章

Route.get() 需要一个回调函数但得到一个 [object Object]

两个独立的 Heroku(Node 后端和 React 前端)之间的通信?

React 前端,Node JS 后端托管

React App 和 node.js 后端 api 安全获取

将图像文件从 React 前端上传到 Node/Express/Mongoose/MongoDB 后端(不工作)

使用 Node.js 后端在我的 React 应用程序中启用 CORS