【问题标题】:request body is empty on post request when using FormData()使用 FormData() 时,请求正文在发布请求时为空
【发布时间】:2021-10-16 01:11:31
【问题描述】:

我正在尝试将表单数据从 react 发送到 express 服务器。发送 json 有效负载时,我得到了预期的结果。但是,当使用 formdata.append 发送数据时,我得到了空的请求正文。

我尝试了这里发布的各种解决方案来解决类似的问题,但似乎没有什么对我有用。

希望有人能帮忙

app.js

require('./database/db')
const express = require('express')
const cors = require('cors')
const userRouter = require('./routes/user')
const menuRouter = require('./routes/menu')

const app = express()

app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cors())

app.use(express.static('public'))

app.use(userRouter)
app.use(menuRouter)

app.listen(9000, () => console.log('Server started at port: 9000'))

我想呼叫的路线

router.post(
    '/addMenu',
    [
        check('name', 'Menu name is required!').not().isEmpty(),
        check('price', 'Price is required!').not().isEmpty(),
        check('category', 'Category is required!').not().isEmpty(),
    ],
    verifyAuth,
    (req, res) => {
        console.log(req.body)
// body is empty here
})

反应引导形式

import { Component } from 'react'
import axios from '../axios'
import { Link } from 'react-router-dom'
import { Button, Form, Card } from 'react-bootstrap'

export default class AddMenu extends Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '',
            price: '',
            category: '',
            image: '',
        }

        this.inputHandler = this.inputHandler.bind(this)
        this.inputHandler = this.inputHandler.bind(this)
        this.addMenu = this.addMenu.bind(this)
    }

    inputHandler = (e) => {
        this.setState({
            [e.target.name]: e.target.value,
        })
    }
    fileHandler = (e) => {
        this.setState({
            image: e.currentTarget.files[0],
        })
    }
    addMenu = (e) => {
        e.preventDefault()
        const { name, price, category, image } = this.state

        const data = new FormData()

        data.append('name', name)
        data.append('price', price)
        data.append('category', category)
        data.append('file', image)

        axios
            .post('/addMenu', data)
            .then((response) => {
                if (response.data.success) {
                    alert(response.data.menu)
                } else {
                    alert('something went wrong')
                }
            })
            .catch()
    }

    render() {
        return (
            <div className='col-lg-12 col-centered' style={{ height: '44rem' }}>
                <Card
                    style={{
                        width: '20rem',
                        marginLeft: '420px',
                        marginTop: '80px',
                        float: 'left',
                        height: '34rem',
                    }}
                >
                    <Card.Img
                        variant='top'
                        src='https://image.freepik.com/free-vector/food-delivery-service-fast-food-delivery-scooter-delivery-service-illustration_67394-869.jpg'
                    />
                    <Card.Body>
                        <Card.Title>Add Menu</Card.Title>
                        <Card.Text>
                            Although a great restaurant experience must include great food, a bad restaurant experience can be achieved through bad service alone.
                            Ideally, service is invisible.
                        </Card.Text>
                        <Button as={Link} to='/' variant='info'>
                            Go to Home
                        </Button>
                    </Card.Body>
                </Card>
                <Card
                    style={{
                        width: '22rem',
                        marginTop: '78px',
                        float: 'left',
                        height: '34rem',
                    }}
                >
                    <Form className='add-menu' onSubmit={this.addMenu}>
                        <h2>Add Menu</h2>
                        <Form.Group controlId='formBasicName'>
                            <Form.Label>Restaurant Name</Form.Label>
                            <Form.Control
                                type='text'
                                name='name'
                                placeholder='Enter Restaurant Name'
                                value={this.state.name}
                                onChange={(event) => this.inputHandler(event)}
                            />
                        </Form.Group>

                        <Form.Group controlId='formBasicPrice'>
                            <Form.Label>Enter Price</Form.Label>
                            <Form.Control type='text' name='price' placeholder='Price' value={this.state.price} onChange={(event) => this.inputHandler(event)} />
                        </Form.Group>

                        <Form.Group controlId='formBasicCategory'>
                            <Form.Label>Enter Category</Form.Label>
                            <Form.Control
                                name='category'
                                type='text'
                                placeholder='Enter Menu Category'
                                value={this.state.category}
                                onChange={(event) => this.inputHandler(event)}
                            />
                        </Form.Group>

                        <Form.Group controlId='formBasicImage'>
                            <Form.Label>Enter Category</Form.Label>
                            <Form.Control name='image' type='file' onChange={this.fileHandler} />
                        </Form.Group>

                        <Button className='btn-lg btn-block' variant='info' type='submit'>
                            Add Menu
                        </Button>
                    </Form>
                </Card>
                <br />
                <br />
                <br />
            </div>
        )
    }
}

路线/菜单实现

const router = require('express').Router()
const { verifyAuth } = require('../middleware/auth')
const upload = require('../middleware/fileUpload')
const Menu = require('../models/Menu')
const { check, validationResult } = require('express-validator')

// add a menu item
router.post(
    '/addMenu',
    [
        check('name', 'Menu name is required!').not().isEmpty(),
        check('price', 'Price is required!').not().isEmpty(),
        check('category', 'Category is required!').not().isEmpty(),
    ],
    verifyAuth,
    (req, res) => {
        console.log(req.body)
        if (req.authUser.userType === 'Restaurant') {
            const validationErr = validationResult(req)
            if (validationErr.isEmpty()) {
                let image = null
                const { name, price, category } = req.body
                if (req.file) {
                    upload(req, res, (err) => {
                        if (err) {
                            response.status(500).json({ success: false, message: err })
                        } else {
                            image = req.file.filename
                        }
                    })
                }
                const newMenu = new Menu({ name, price, category, addedBy: req.authUser._id, image })
                newMenu
                    .save()
                    .then((menu) => res.status(201).json({ success: true, message: 'Menu added', menu }))
                    .catch((error) => res.status(500).json({ success: false, message: error.message }))
            } else {
                res.status(400).json({ success: false, errors: validationErr.array() })
            }
        } else {
            res.status(403).json({ success: true, message: 'Insufficient privileges' })
        }
    }
)

// get all menu items
router.get('/menus', (req, res) => {
    Menu.find()
        .populate('addedBy', '-_id name')
        .then((menus) => res.status(200).json({ success: true, menus }))
        .catch((error) => res.status(500).json({ success: false, message: error.message }))
})

module.exports = router

即使在 Postman 中 x-www-form-urlencoded 发送数据但 form-data 不发送数据

【问题讨论】:

    标签: node.js reactjs


    【解决方案1】:

    如果没有通过这样的路由给它的文件,表单数据将得到空对象。

    import multer from 'multer';
        router.post(
        '/addMenu',
        multer().single("keyNameForFile"),
        [
            check('name', 'Menu name is required!').not().isEmpty(),
            check('price', 'Price is required!').not().isEmpty(),
            check('category', 'Category is required!').not().isEmpty(),
        ],
        verifyAuth,
        (req, res) => {
            console.log(req.body)
    // body is empty here
    })
    

    不知道是什么原因导致了这种行为,但在发送图像文档等文件时使用表单数据。

    【讨论】:

    • 我正要提到这个相同的解决方案,在这里你已经做到了。还是谢谢你。感谢您的帮助
    【解决方案2】:

    您应该将请求标头的 Content-Type 设置为 Content-Type: application/x-www-form-urlencoded 这将允许 express 访问您在正文中的表单数据

    要在 axios 中设置标题,您可以像这样执行它

    const config = {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };
    

    然后您使用这样设置的标头向后端执行请求

    axios.post('/addMenu', data, config)
        .then({ /*...*/});
    

    【讨论】:

    • 感谢您的回复。我之前也尝试过,但没有奏效。我也尝试将内容类型设置为 multipart/form-data 但没有奏效。我不知道出了什么问题
    【解决方案3】:

    在您的axios 请求中,您需要在标头中指定内容类型 的请求。我通常也会做的是将我的agrs 指定为一个对象 用于注释目的

    像这样:

    axios({
      method: 'post',
      url: '/addMenu',
      data: data,
      headers: { 'Content-Type': 'application/json' },
    })
      .then((response) => {
        if (response.data.success) {
          alert(response.data.menu)
        } else {
          alert('something went wrong')
        }
      })
      .catch((error) => {
        //handle error
        console.log(error);
      });
    

    编辑:

    我刚刚注意到您也在尝试发送文件...哈哈

    要通过快递发送文件,您需要获取名为multer 的包来为您处理文件上传

    npm i --save multer

    然后在配置服务器时添加它

    const multer = require('multer');
    
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use(cors());
    
    app.use(multer().any());
    

    完成后,在 axios 端的请求中,这样做:

    
    axios({
      method: 'post',
      url: '/addMenu',
      data: data,
      headers: { 'Content-Type': 'multipart/form-data' },
    })
      .then((response) => {
        if (response.data.success) {
          alert(response.data.menu)
        } else {
          alert('something went wrong')
        }
      })
      .catch((error) => {
        //handle error
        console.log(error);
      });
    
    

    最后,要在您的服务器上处理您的文件,它们将显示在请求中 像这样的对象:

    request.files // which will be an array of file objects
    

    请试试这个...我知道它很长,但我曾经遇到过这个问题并使用了这个解决方案

    【讨论】:

    • 感谢您的回复。它也不是这样工作的。
    • 你说得对,我认为你在快递服务器上添加路由的方式有问题,你能添加routes/menu的实现吗...
    • 我已经添加了。你能帮忙吗?
    • 我已经将 multer 作为名为 fileUpload 的中间件。如果您查看 addMenu 实现,您将看到来自中间件的 upload(req,res,err)。当我尝试使用表单数据从邮递员发送数据时,它也会记录为空。
    • 好的,您是否也尝试使用 mulitpart/form-data 标头
    【解决方案4】:

    根据 @ash.io 回答 MERN 堆垛机的重要通知! :我的情况是因为它曾经返回空对象,但是当我添加(挂载)app.use(express.json()) 中间件时,一切都解决了!目前 MongoDB 显示结果很好!对于任何对快速使用Form sn-p 感兴趣的人,这里是React-useForm boilerplate

    【讨论】:

      猜你喜欢
      • 2018-03-22
      • 1970-01-01
      • 2021-09-01
      • 2019-10-22
      • 2022-10-17
      • 1970-01-01
      • 2020-08-02
      • 2015-09-27
      • 1970-01-01
      相关资源
      最近更新 更多