表达 post 请求将我重定向到 json 页面而不是显示成功或错误消息 toast
express post request redirect me to a json page instead of showing the success or error messages toast
我正在使用 MERN 堆栈制作一个 crud 应用程序来练习我的技能,因为我是新手
我正在尝试创建一个带有图像的新产品,我使用 multer 来处理图像
我很好地完成了我的后端并在 Postman 上对其进行了测试并且它工作正常,但是当我将数据传递给前端(React 和 Redux)时它也工作正常但浏览器没有向我显示成功或错误消息,而是重定向了我使用我发送的 json 数据
到一个新页面
PS。我尝试使用 formik 来处理我的表单状态和错误,但它没有将我重定向到 json 页面,它在 toast 中显示了我从后端发送的错误消息,但对我不起作用,因为formik 处理文件真的很复杂,所以我用简单的方法做到了
- 我使用 multer 来处理图像
- 对前端做出反应
- redux 作为 react 的存储
- react-toastify 以显示成功和错误消息
// multer中间件来处理图像
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads/");
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({ storage, fileFilter });
这是验证和使用后端的方法,它是一组方法
const createProduct = [
//validate product that it's not empthy
// then sanitize it with trima and escape
body("name")
.isLength({ min: 5 })
.withMessage("Must be at least 2 letters")
.trim()
.escape(),
body("description")
.isLength({ min: 20 })
.withMessage("Must be at least 10 letters")
.trim()
.escape(),
body("category")
.isLength({ min: 1 })
.withMessage("This field is required")
.trim()
.escape(),
body("price")
.isLength({ min: 1 })
.withMessage("Must be at least 1 number")
.isNumeric()
.withMessage("Must be Numeric")
.trim()
.escape(),
body("numberInStock")
.isLength({ min: 1 })
.withMessage("Must be at least 1 number")
.isNumeric()
.withMessage("Must be Numeric")
.trim()
.escape(),
// continue process after validation
(req, res, next) => {
console.log(req.file);
// get the validation errors from the request
const errors = validationResult(req);
// create new product after being validated and sanitized
let newProduct = new Product({
name: req.body.name,
description: req.body.description,
category: req.body.category,
price: req.body.price,
numberInStock: req.body.numberInStock,
productImage: req.file.path
});
// if there are validation errors send them as json
if (!errors.isEmpty()) {
//i only show the first error
let field = errors.errors[0].param;
let message = errors.errors[0].msg;
let errorMessage = field + " " + message;
res.status(400).json({
message: errorMessage
});
} else {
newProduct.save(function (err, product) {
if (err) {
console.log(err);
res.json({
message: "Couldn't create please try again"
});
} else {
res.redirect({
message: "Created Successfully",
product
});
}
});
}
}
];
正在处理路线
router.post("/api/product/create",upload.single("productImage"), createProduct);
//添加产品操作
export const addProduct = product => dispatch => {
return new Promise((resolve, reject) => {
axios
.post("/api/product/create", product)
.then(res => {
let newProduct = res.data.product;
let successMessage = res.data.message;
dispatch(addProductSuccess(newProduct, successMessage));
resolve(successMessage);
})
.catch(error => {
console.log(error.message);
dispatch(addProductFailure(error));
reject(error.message);
});
});
};
const addProductSuccess = (product, successMessage) => {
return {
type: ADD_PRODUCT_SUCCESS,
payload: {
product,
successMessage
}
};
};
const addProductFailure = error => {
return {
type: ADD_PRODUCT_FAILURE,
payload: {
error
}
};
};
//添加产品形式
function AddProductForm() {
// importing categories and laoding state from out store
const { categories, error, loading } = useSelector(
state => state.categoriesss
);
const [state, setState] = useState({
name: "",
description: "",
category: "",
price: "",
numberInStock: "",
productImage: ""
});
const handleChange = e => {
const { name, value } = e.target;
setState(prevState => ({
...prevState,
[name]: value
}));
};
// react redux method to dispatch our functions
const dispatch = useDispatch();
// fetch all the the categories with dispatch before our component render
useEffect(() => {
dispatch(fetchCategories());
}, [dispatch]);
// handle submit our form
const handleSubmitt = product => {
dispatch(addProduct(product))
.then(res => {
toast.success(res, {
position: toast.POSITION.BOTTOM_LEFT,
transition: Slide
});
})
.catch(err => {
toast.error(err, {
position: toast.POSITION.BOTTOM_LEFT,
autoClose: false
});
});
};
let newProduct = {
name: state.name,
description: state.description,
category: state.category,
price: state.price,
numberInStock: state.numberInStock,
productImage: state.productImage
};
return (
<Container>
<form
action='/api/product/create'
method='post'
enctype='multipart/form-data'
onSubmit={() => handleSubmitt(newProduct)}>
<div className='form-group'>
<input
className='form-control mb-2'
type='text'
placeholder='Enter Product name'
name='name'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
className='form-control mb-2'
as='textarea'
placeholder='Enter Product description'
name='description'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<select
className='custom-select mb-2'
name='category'
required
onChange={handleChange}>
{loading && <option>loading...</option>}
{categories.map(cat => {
return (
<option key={cat._id} value={cat._id}>
{cat.name}
</option>
);
})}
</select>
</div>
<div className='form-group'>
<input
className='form-control mb-2'
type='text'
placeholder='Enter Product price'
name='price'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
className='form-control mb-2'
type='text'
placeholder='Enter Product numberInStock'
name='numberInStock'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
className='custom custom-file mb-2'
type='file'
name='productImage'
required
onChange={handleChange}
/>
</div>
<Button
variant='primary'
type='submit'
onClick={() => handleSubmitt(newProduct)}>
Submit{" "}
</Button>{" "}
</form>
</Container>
);
}
export default AddProductForm;
results after the request is done with success
results after the request is done with error
试试这个
<form
action='/api/product/create'
method='post'
enctype='multipart/form-data'
onSubmit={(e) => {
handleSubmitt(newProduct)
e.preventDefault()
}>
我正在使用 MERN 堆栈制作一个 crud 应用程序来练习我的技能,因为我是新手 我正在尝试创建一个带有图像的新产品,我使用 multer 来处理图像 我很好地完成了我的后端并在 Postman 上对其进行了测试并且它工作正常,但是当我将数据传递给前端(React 和 Redux)时它也工作正常但浏览器没有向我显示成功或错误消息,而是重定向了我使用我发送的 json 数据
到一个新页面PS。我尝试使用 formik 来处理我的表单状态和错误,但它没有将我重定向到 json 页面,它在 toast 中显示了我从后端发送的错误消息,但对我不起作用,因为formik 处理文件真的很复杂,所以我用简单的方法做到了
- 我使用 multer 来处理图像
- 对前端做出反应
- redux 作为 react 的存储
- react-toastify 以显示成功和错误消息
// multer中间件来处理图像
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads/");
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({ storage, fileFilter });
这是验证和使用后端的方法,它是一组方法
const createProduct = [
//validate product that it's not empthy
// then sanitize it with trima and escape
body("name")
.isLength({ min: 5 })
.withMessage("Must be at least 2 letters")
.trim()
.escape(),
body("description")
.isLength({ min: 20 })
.withMessage("Must be at least 10 letters")
.trim()
.escape(),
body("category")
.isLength({ min: 1 })
.withMessage("This field is required")
.trim()
.escape(),
body("price")
.isLength({ min: 1 })
.withMessage("Must be at least 1 number")
.isNumeric()
.withMessage("Must be Numeric")
.trim()
.escape(),
body("numberInStock")
.isLength({ min: 1 })
.withMessage("Must be at least 1 number")
.isNumeric()
.withMessage("Must be Numeric")
.trim()
.escape(),
// continue process after validation
(req, res, next) => {
console.log(req.file);
// get the validation errors from the request
const errors = validationResult(req);
// create new product after being validated and sanitized
let newProduct = new Product({
name: req.body.name,
description: req.body.description,
category: req.body.category,
price: req.body.price,
numberInStock: req.body.numberInStock,
productImage: req.file.path
});
// if there are validation errors send them as json
if (!errors.isEmpty()) {
//i only show the first error
let field = errors.errors[0].param;
let message = errors.errors[0].msg;
let errorMessage = field + " " + message;
res.status(400).json({
message: errorMessage
});
} else {
newProduct.save(function (err, product) {
if (err) {
console.log(err);
res.json({
message: "Couldn't create please try again"
});
} else {
res.redirect({
message: "Created Successfully",
product
});
}
});
}
}
];
正在处理路线
router.post("/api/product/create",upload.single("productImage"), createProduct);
//添加产品操作
export const addProduct = product => dispatch => {
return new Promise((resolve, reject) => {
axios
.post("/api/product/create", product)
.then(res => {
let newProduct = res.data.product;
let successMessage = res.data.message;
dispatch(addProductSuccess(newProduct, successMessage));
resolve(successMessage);
})
.catch(error => {
console.log(error.message);
dispatch(addProductFailure(error));
reject(error.message);
});
});
};
const addProductSuccess = (product, successMessage) => {
return {
type: ADD_PRODUCT_SUCCESS,
payload: {
product,
successMessage
}
};
};
const addProductFailure = error => {
return {
type: ADD_PRODUCT_FAILURE,
payload: {
error
}
};
};
//添加产品形式
function AddProductForm() {
// importing categories and laoding state from out store
const { categories, error, loading } = useSelector(
state => state.categoriesss
);
const [state, setState] = useState({
name: "",
description: "",
category: "",
price: "",
numberInStock: "",
productImage: ""
});
const handleChange = e => {
const { name, value } = e.target;
setState(prevState => ({
...prevState,
[name]: value
}));
};
// react redux method to dispatch our functions
const dispatch = useDispatch();
// fetch all the the categories with dispatch before our component render
useEffect(() => {
dispatch(fetchCategories());
}, [dispatch]);
// handle submit our form
const handleSubmitt = product => {
dispatch(addProduct(product))
.then(res => {
toast.success(res, {
position: toast.POSITION.BOTTOM_LEFT,
transition: Slide
});
})
.catch(err => {
toast.error(err, {
position: toast.POSITION.BOTTOM_LEFT,
autoClose: false
});
});
};
let newProduct = {
name: state.name,
description: state.description,
category: state.category,
price: state.price,
numberInStock: state.numberInStock,
productImage: state.productImage
};
return (
<Container>
<form
action='/api/product/create'
method='post'
enctype='multipart/form-data'
onSubmit={() => handleSubmitt(newProduct)}>
<div className='form-group'>
<input
className='form-control mb-2'
type='text'
placeholder='Enter Product name'
name='name'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
className='form-control mb-2'
as='textarea'
placeholder='Enter Product description'
name='description'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<select
className='custom-select mb-2'
name='category'
required
onChange={handleChange}>
{loading && <option>loading...</option>}
{categories.map(cat => {
return (
<option key={cat._id} value={cat._id}>
{cat.name}
</option>
);
})}
</select>
</div>
<div className='form-group'>
<input
className='form-control mb-2'
type='text'
placeholder='Enter Product price'
name='price'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
className='form-control mb-2'
type='text'
placeholder='Enter Product numberInStock'
name='numberInStock'
required
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
className='custom custom-file mb-2'
type='file'
name='productImage'
required
onChange={handleChange}
/>
</div>
<Button
variant='primary'
type='submit'
onClick={() => handleSubmitt(newProduct)}>
Submit{" "}
</Button>{" "}
</form>
</Container>
);
}
export default AddProductForm;
results after the request is done with success
results after the request is done with error
试试这个
<form
action='/api/product/create'
method='post'
enctype='multipart/form-data'
onSubmit={(e) => {
handleSubmitt(newProduct)
e.preventDefault()
}>