You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot+React前后端POST请求报400 Bad Request求助

问题描述

我正在学习SpringBoot Java和React,尝试实现前后端交互以向数据库提交POST请求(当前Controller方法为测试用)。我的GET请求一切正常,且为了自行编写SQL查询,刻意不使用Axios等工具库。

目前遇到的问题是:运行应用时前后端通信失败,收到400 Bad Request错误,具体报错信息如下:

POST http://localhost:3000/addFormDataToDatabase 400 (Bad Request)
addFormDataToDatabase @ FormComponent.jsx:120

我明白问题大概率出在前端请求格式,但尝试多种方式后仍未解决。以下是当前最接近可行的代码,仍返回该错误,希望得到问题排查的帮助与建议。


前端代码(FormComponent.jsx,已剔除无关验证方法)

import React, { Component } from 'react'
import AdminComponent from './AdminComponent.jsx'

class FormComponent extends Component {
    constructor(props){
        super(props)
        this.state = {
                rentalCars: []
        };
        this.addFormDataToDatabase = this.addFormDataToDatabase.bind(this);
    }

    async componentDidMount() {
        await this.getAllRentalCars();
    }

    async getAllRentalCars() {
        const response = await fetch('/rent');
        const data = await response.json();
        console.log("GetRentalCars received from database: " + JSON.stringify(data));
        this.setState({ rentalCars: data });
    }


    render() {
            const {rentalCars} = this.state;
            const optionItems = rentalCars.map(({id, carName, rentalPricePerDay}, key) =>
               <option key={id} value={carName}> {carName}: {rentalPricePerDay} SEK/day</option>)

            return (
                <div className="App">
                    <div className="card col-md-6 offset-md-3 offset-md-3">
                        <div className="card-body">
                            <header className="rental-cars">
                                <h2 className="text-center">Rent one of our cars!</h2>
                            </header>
                            <form className="registerForm" onSubmit={this.handleSubmit}>
                                <div className="form-group">
                                    <label>First name: </label>
                                    <input
                                        className="form-control"
                                        required
                                        type="text"
                                        name="firstName"
                                        value={this.state.firstName}
                                    />
                                    <label>Last name: </label>
                                     <input
                                        className="form-control"
                                        required
                                        type="text"
                                        name="lastName"
                                        value={this.state.lastName}
                                     />
                                    <label>Date of Birth: </label>
                                     <input
                                        className="form-control"
                                        required
                                        type="date"
                                        name="dateOfBirth"
                                        value={this.state.dateOfBirth}
                                    />
                                    <label>I want to rent a car from </label>
                                    <input
                                        className="form-control"
                                        required
                                        type="date"
                                        name="startDate"
                                        value={this.state.startDate}
                                    />
                                    <label> to </label>
                                    <input
                                        className="form-control"
                                        required
                                        type="date"
                                        name="endDate"
                                        value={this.state.endDate}
                                    />
                                    <label>Car I wish to rent: </label>
                                        <select
                                            className="form-control"
                                            name="selectCar"
                                            ref={node => (this.inputNode = node)}>
                                            {optionItems}
                                        </select>

                                    <input type="submit" className="btn btn-primary" value="Submit"/>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
            );
        }

        handleSubmit = (event) => {
            event.preventDefault();
            let target = event.target;
            let formData = [];
            for (let i = 0; i < target.length; i++) {
                formData[target.elements[i].getAttribute("name")] = target.elements[i].value;
            }
            let validationErrors = this.validateValues(formData);
            for (let j = 0; j < validationErrors.length; j++){
                console.log("Error:" + validationErrors[j]);
            }
            console.log(validationErrors);
            console.log('formData', formData);
            if(!!validationErrors){
                this.addFormDataToDatabase(formData);
            }
        }
        async addFormDataToDatabase(formData){
            console.log('formData inside method addFormDataToDatabase', formData);
            const requestOptions = {
                method: 'POST',
                headers: {'Content-type': 'application/json'},
                body: formData
            };
            const response = await fetch('/addFormDataToDatabase', requestOptions);
            const message = await response.json();
            console.log("addFormDataToDatabase received from backend: " + JSON.stringify(message));
        }
}

后端Controller代码

@RestController 
@RequestMapping("/") 
public class RentalCarApplicationController {

@Autowired
private RentalCarRepository rentalCarRepository;
private RenterRepository renterRepository;
private CarRentalDataRepository carRentalDataRepository;

@GetMapping("/rent")
public List<RentalCar> getRentalCars(){ return rentalCarRepository.getRentalCars(); }

@PostMapping("/addFormDataToDatabase")
public List<String> addFormDataToDatabase(@NotNull @RequestBody List<String> bookingData){
    return (bookingData);
}
}

问题排查与解决建议

核心问题分析

400 Bad Request的直接原因是前后端数据格式完全不匹配:

  1. 前端用数组字面量[]创建formData,但实际存储的是键值对,本质是对象;同时声明了Content-type: application/json,却没有把对象序列化为JSON字符串,后端无法解析。
  2. 后端Controller接收的是List<String>类型,但前端传递的是键值对结构的表单数据,Spring无法将其解析为字符串列表。

具体修改步骤

1. 前端请求格式修正

  • 将formData改为对象(存储键值对更合理),过滤掉无name属性的元素(比如提交按钮)。
  • 提交时用JSON.stringify()将对象转为JSON字符串。
  • 修正验证错误的判断逻辑(空数组转为布尔值是true,应该判断长度)。

修改后的相关方法:

handleSubmit = (event) => {
    event.preventDefault();
    let target = event.target;
    let formData = {}; // 改为对象存储键值对
    for (let i = 0; i < target.length; i++) {
        const name = target.elements[i].getAttribute("name");
        if (name) { // 过滤无name的元素
            formData[name] = target.elements[i].value;
        }
    }
    let validationErrors = this.validateValues(formData);
    for (let j = 0; j < validationErrors.length; j++){
        console.log("Error:" + validationErrors[j]);
    }
    console.log(validationErrors);
    console.log('formData', formData);
    // 只有无验证错误时才提交
    if(validationErrors.length === 0){
        this.addFormDataToDatabase(formData);
    }
}

async addFormDataToDatabase(formData){
    console.log('formData inside method addFormDataToDatabase', formData);
    const requestOptions = {
        method: 'POST',
        headers: {'Content-type': 'application/json'},
        body: JSON.stringify(formData) // 序列化为JSON字符串
    };
    try {
        const response = await fetch('/addFormDataToDatabase', requestOptions);
        if (!response.ok) {
            throw new Error(`请求失败,状态码: ${response.status}`);
        }
        const message = await response.json();
        console.log("后端返回数据: " + JSON.stringify(message));
    } catch (error) {
        console.error('请求出错:', error);
    }
}

2. 后端接收格式修正

前端传递的是键值对JSON,后端需要用实体类接收,而非List<String>。创建一个DTO类:

import java.time.LocalDate;

public class BookingRequest {
    private String firstName;
    private String lastName;
    private LocalDate dateOfBirth;
    private LocalDate startDate;
    private LocalDate endDate;
    private String selectCar;

    // 生成无参构造方法、Getter和Setter
    public BookingRequest() {}

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    public LocalDate getDateOfBirth() { return dateOfBirth; }
    public void setDateOfBirth(LocalDate dateOfBirth) { this.dateOfBirth = dateOfBirth; }

    public LocalDate getStartDate() { return startDate; }
    public void setStartDate(LocalDate startDate) { this.startDate = startDate; }

    public LocalDate getEndDate() { return endDate; }
    public void setEndDate(LocalDate endDate) { this.endDate = endDate; }

    public String getSelectCar() { return selectCar; }
    public void setSelectCar(String selectCar) { this.selectCar = selectCar; }
}

修改Controller方法:

@PostMapping("/addFormDataToDatabase")
public BookingRequest addFormDataToDatabase(@NotNull @RequestBody BookingRequest bookingData){
    // 后续可调用repository将数据存入数据库
    return bookingData; // 测试用:返回接收的参数
}

额外注意事项

  • 前端表单绑定问题:当前input的value绑定了state字段,但state未初始化这些值,会导致输入无法更新状态。需在constructor中添加初始值,并给每个input加onChange事件:
constructor(props){
    super(props)
    this.state = {
        rentalCars: [],
        firstName: '',
        lastName: '',
        dateOfBirth: '',
        startDate: '',
        endDate: ''
    };
    this.addFormDataToDatabase = this.addFormDataToDatabase.bind(this);
}

// 示例:firstName的input添加onChange
<input
    className="form-control"
    required
    type="text"
    name="firstName"
    value={this.state.firstName}
    onChange={(e) => this.setState({ firstName: e.target.value })}
/>
  • 跨域问题:如果前端在3000端口、后端在8080端口,需给Controller添加跨域注解:
@RestController 
@RequestMapping("/")
@CrossOrigin(origins = "http://localhost:3000") // 允许前端跨域请求
public class RentalCarApplicationController {
    // ...
}

内容的提问来源于stack exchange,提问作者Zarkaylia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 06:05:07