Node React, действие достигает серверной части, но не сохраняет продукт - PullRequest
1 голос
/ 27 января 2020

Итак, я создал Модель, Маршруты, Контроллеры, все выглядит нормально, глядя на код. Я также создал свой редуктор, действие и компонент для внешнего интерфейса. Поскольку я пытаюсь создать продукт, он запускает действие и, кажется, успешно отправляет его, но в devtools-редуктах я вижу, что элемент, который я получаю в массиве products, имеет значение NULL. Также, когда я проверяю свою базу данных, продукт не сохраняется. Что странно, так это то, что я вижу свое изображение в папке с изображениями, которая находится на серверной части. Я не знаю, правильно ли я настроил действие, даже компонент или контроллер, возможно, маршруты. Я был бы рад, если бы кто-то мог разобраться в этом, возможно, моя реализация неверна. Я собираюсь связать свой Github и вставить некоторые коды ниже.

Кстати, я могу зарегистрироваться, войти в систему и получить пользователей, так что это что-то определенное c, чтобы сделать с этой функцией. Пожалуйста, я также рад изменить вещи в моем приложении, если вы думаете, что некоторые из моих подходов неверны, я довольно новичок в этом стеке <3 </strong>

Ссылка на Github: https://github.com/tigerabrodi/eBuy

сервер. js (Back End)

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));

маршруты продуктов (Back End)

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;

Модель продукта (Back End)

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);

контроллер продукта (Back end)

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);
    }
}

редуктор продукта (Front end)

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

действия продукта (Front End)

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));

1 Ответ

0 голосов
/ 28 января 2020

Нечего сказать, как сказал Сулейман, я забыл подождать. Другие вещи убирали заголовки, так как 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}
        })
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...