最后更新:2026-07-29
适用场景:Spring Boot 接收 JSON、@RequestBody、接收对象、接收数组、接收 List、接收 Map、接口联调、JSON 参数为空、415 Unsupported Media Type、JSON parse error

Spring Boot 写接口时,最常见的情况就是前端传一段 JSON,后端用 Java 对象接住。

看起来很简单:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest request) {
    return "ok";
}

但实际开发里,这块经常出问题:

参数接收不到
@RequestBody 为空
Required request body is missing
415 Unsupported Media Type
JSON parse error
Cannot deserialize value of type
LocalDateTime 反序列化失败
前端传的是数组,后端用对象接
前端传的是 form-data,后端却用 @RequestBody 接

这篇就专门把 Spring Boot 接收 JSON 参数这件事讲清楚。

Spring MVC 中,@RequestBody 会把 HTTP 请求体交给 HttpMessageConverter 处理,再转换成控制器方法里声明的 Java 类型;如果是 JSON,一般就是由 Jackson 相关的消息转换器完成对象转换。官方文档也明确说明,@RequestBody 用于访问 HTTP request body,请求体内容会通过 HttpMessageConverter 转成方法参数类型。(Home)


一、先看结论

如果前端传的是 JSON,后端一般这样写:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest request) {
    return "ok";
}

前端请求头必须是:

Content-Type: application/json

请求体示例:

{
  "username": "zhangsan",
  "age": 18
}

后端 DTO:

public class UserCreateRequest {

    private String username;

    private Integer age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

如果用 Lombok,可以简化成:

import lombok.Data;

@Data
public class UserCreateRequest {

    private String username;

    private Integer age;
}

最容易踩坑的地方就三点:

1. 请求头不是 application/json
2. JSON 结构和 Java 接收类型不一致
3. 字段类型不匹配,比如字符串传给 Integer

二、接收普通 JSON 对象

这是最常见的情况。

前端传:

{
  "username": "zhangsan",
  "nickname": "张三",
  "age": 18,
  "enabled": true
}

后端 DTO:

import lombok.Data;

@Data
public class UserCreateRequest {

    private String username;

    private String nickname;

    private Integer age;

    private Boolean enabled;
}

Controller:

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public String createUser(@RequestBody UserCreateRequest request) {
        System.out.println(request.getUsername());
        System.out.println(request.getAge());
        return "ok";
    }
}

curl 测试:

curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d "{\"username\":\"zhangsan\",\"nickname\":\"张三\",\"age\":18,\"enabled\":true}"

注意:JSON 字段名和 Java 属性名要能对应上。

{
  "username": "zhangsan"
}

对应:

private String username;

三、接收 JSON 数组

如果前端最外层传的是数组:

[
  {
    "username": "zhangsan",
    "age": 18
  },
  {
    "username": "lisi",
    "age": 20
  }
]

后端不能用普通对象接:

@RequestBody UserCreateRequest request

应该用 List

@PostMapping("/batch")
public String batchCreate(@RequestBody List<UserCreateRequest> users) {
    return "count: " + users.size();
}

完整示例:

import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/batch")
    public String batchCreate(@RequestBody List<UserCreateRequest> users) {
        for (UserCreateRequest user : users) {
            System.out.println(user.getUsername());
        }
        return "count: " + users.size();
    }
}

记住一个简单规则:

JSON 最外层是 { },后端用对象接。
JSON 最外层是 [ ],后端用 List 接。

四、接收嵌套 JSON 对象

前端经常会传嵌套结构,比如用户信息里带地址:

{
  "username": "zhangsan",
  "address": {
    "province": "浙江省",
    "city": "杭州市",
    "detail": "西湖区某某路"
  }
}

后端 DTO 可以这样写:

import lombok.Data;

@Data
public class UserCreateRequest {

    private String username;

    private AddressRequest address;
}
import lombok.Data;

@Data
public class AddressRequest {

    private String province;

    private String city;

    private String detail;
}

Controller 不需要特殊处理:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest request) {
    System.out.println(request.getUsername());
    System.out.println(request.getAddress().getCity());
    return "ok";
}

嵌套对象不要硬用 Map 接。能定义 DTO 就定义 DTO,后期维护更清楚。


五、接收对象数组嵌套

再复杂一点,比如用户下面有订单列表:

{
  "username": "zhangsan",
  "orders": [
    {
      "orderNo": "A001",
      "amount": 99.90
    },
    {
      "orderNo": "A002",
      "amount": 199.00
    }
  ]
}

DTO:

import lombok.Data;

import java.util.List;

@Data
public class UserCreateRequest {

    private String username;

    private List<OrderRequest> orders;
}
import lombok.Data;

import java.math.BigDecimal;

@Data
public class OrderRequest {

    private String orderNo;

    private BigDecimal amount;
}

这里金额建议用 BigDecimal,不要用 Double

private BigDecimal amount;

不建议:

private Double amount;

金额字段用浮点数,后面做计算时容易遇到精度问题。


六、接收 Map

有些接口字段不固定,或者只是临时调试,可以用 Map

@PostMapping("/raw")
public String raw(@RequestBody Map<String, Object> body) {
    System.out.println(body);
    return "ok";
}

前端传:

{
  "username": "zhangsan",
  "extra": {
    "source": "web",
    "level": "vip"
  }
}

后端可以这样取:

Object username = body.get("username");
Object extra = body.get("extra");

但正式业务接口不建议长期用 Map<String, Object>

原因很简单:

字段不清楚
类型不清楚
接口文档不直观
参数校验不方便
后期维护容易出问题

更推荐:

正式接口:用 DTO
临时调试:可以用 Map
字段完全动态:可以考虑 JsonNode

七、接收 JsonNode

如果 JSON 很复杂,但你只想取其中几个字段,可以用 Jackson 的 JsonNode

import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/json")
public class JsonController {

    @PostMapping("/node")
    public String node(@RequestBody JsonNode root) {
        String username = root.get("username").asText();
        String city = root.get("address").get("city").asText();

        System.out.println(username);
        System.out.println(city);

        return "ok";
    }
}

请求:

{
  "username": "zhangsan",
  "address": {
    "city": "杭州"
  }
}

JsonNode 适合:

第三方接口结构不稳定
只读取部分字段
临时排查 JSON 结构
不想一开始就定义完整 DTO

但项目内部接口还是建议用 DTO。


八、接收 LocalDateTime

时间字段是 JSON 接收参数里最容易出问题的地方之一。

前端传:

{
  "username": "zhangsan",
  "createdAt": "2026-07-29 10:30:00"
}

Java DTO:

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class UserCreateRequest {

    private String username;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
    private LocalDateTime createdAt;
}

如果不加格式化配置,可能会遇到:

JSON parse error
Cannot deserialize value of type java.time.LocalDateTime

这类问题通常不是 Controller 写错了,而是:

前端时间格式
Java字段类型
Jackson时间格式配置

三者没有对齐。

我的建议是:项目里统一一种时间格式,比如:

yyyy-MM-dd HH:mm:ss

不要一个接口传:

2026-07-29 10:30:00

另一个接口传:

2026/07/29 10:30:00

再另一个传:

2026-07-29T10:30:00

格式越乱,联调问题越多。


九、字段名不一致怎么办

前端传的是下划线:

{
  "user_name": "zhangsan"
}

Java 里一般写驼峰:

private String userName;

这时可以用 @JsonProperty

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class UserCreateRequest {

    @JsonProperty("user_name")
    private String userName;
}

如果项目里所有字段都用下划线,也可以做全局命名策略。但如果只是个别字段不一致,@JsonProperty 更直观。


十、加参数校验

接收 JSON 参数时,通常还要做校验。

DTO:

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

@Data
public class UserCreateRequest {

    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotNull(message = "年龄不能为空")
    private Integer age;
}

Controller:

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public String createUser(@Valid @RequestBody UserCreateRequest request) {
        return "ok";
    }
}

@RequestBody 可以和 @Valid@Validated 一起使用。Spring MVC 官方文档说明,@RequestBody 配合 jakarta.validation.Valid 或 Spring 的 @Validated 会触发标准 Bean Validation,默认校验失败会产生 MethodArgumentNotValidException,并返回 400 响应。(Home)

Spring Boot 项目还需要引入校验依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

十一、@RequestBody 和 @RequestParam 怎么选

这个地方很多人容易混。

前端传 JSON

请求头:

Content-Type: application/json

请求体:

{
  "username": "zhangsan",
  "age": 18
}

后端用:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest request) {
    return "ok";
}

前端传表单参数

请求头:

Content-Type: application/x-www-form-urlencoded

请求体:

username=zhangsan&age=18

后端用:

@PostMapping("/users/form")
public String createUserForm(@RequestParam String username,
                             @RequestParam Integer age) {
    return "ok";
}

官方文档也提醒,表单数据应该用 @RequestParam 读取,而不是依赖 @RequestBody;因为在 Servlet API 中,请求参数访问会导致请求体被解析,请求体不一定能再次可靠读取。(Home)

简单记:

JSON 请求体:@RequestBody
URL 查询参数:@RequestParam
表单提交:@RequestParam
路径变量:@PathVariable
文件上传:MultipartFile / @RequestPart

十二、一个接口能写多个 @RequestBody 吗?

一般不要。

错误写法:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest user,
                         @RequestBody AddressRequest address) {
    return "ok";
}

HTTP 请求体只有一份,不能像普通参数一样拆成多个 @RequestBody

应该定义一个包装对象:

import lombok.Data;

@Data
public class UserWithAddressRequest {

    private UserCreateRequest user;

    private AddressRequest address;
}

JSON:

{
  "user": {
    "username": "zhangsan",
    "age": 18
  },
  "address": {
    "city": "杭州",
    "detail": "西湖区某某路"
  }
}

Controller:

@PostMapping("/users")
public String createUser(@RequestBody UserWithAddressRequest request) {
    return "ok";
}

十三、上传文件同时传 JSON 怎么办

如果是文件上传,同时带 JSON,不建议继续用普通 @RequestBody

这种一般是 multipart/form-data

前端可以传:

file: 文件
meta: JSON字符串

后端用 @RequestPart

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/files")
public class FileController {

    @PostMapping("/upload")
    public String upload(@RequestPart("file") MultipartFile file,
                         @RequestPart("meta") FileMetaRequest meta) {
        System.out.println(file.getOriginalFilename());
        System.out.println(meta.getTitle());
        return "ok";
    }
}

DTO:

import lombok.Data;

@Data
public class FileMetaRequest {

    private String title;

    private String category;
}

Spring MVC 官方文档在 multipart 场景中也给出类似说明:如果 multipart 的某个 part 想像 JSON 一样反序列化,可以使用 @RequestPart,它会通过 HttpMessageConverter 转换该 part 的内容。(Home)


十四、常见错误一:Required request body is missing

报错:

Required request body is missing

常见原因:

1. 请求没有 body
2. 前端没有传 JSON
3. 请求方法不对
4. Content-Type 不对
5. body 被网关或过滤器读掉了
6. 用 GET 请求传 body

检查:

Postman / Apifox 是否选择 raw + JSON
请求头是否是 Content-Type: application/json
请求体是否真的有内容
Controller 是否写了 @RequestBody

正确请求:

curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d "{\"username\":\"zhangsan\"}"

十五、常见错误二:415 Unsupported Media Type

报错:

415 Unsupported Media Type

一般是请求头和后端接收方式不匹配。

比如后端是:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest request) {
    return "ok";
}

但前端传的是:

Content-Type: application/x-www-form-urlencoded

这就容易出问题。

如果你要用 @RequestBody 接 JSON,请求头应该是:

Content-Type: application/json

curl:

curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d "{\"username\":\"zhangsan\",\"age\":18}"

十六、常见错误三:JSON parse error

报错:

JSON parse error

这个范围很大,常见原因包括:

JSON 格式不合法
字段类型不匹配
时间格式不匹配
数组和对象搞反
字符串没加双引号
多了逗号
前端传了空字符串

比如 JSON 写错:

{
  "username": "zhangsan",
  "age": 18,
}

最后多了一个逗号,JSON 不合法。

正确:

{
  "username": "zhangsan",
  "age": 18
}

建议先把 JSON 放到工具里校验一下。你也可以用:

Json哥 - JSON 在线解析、格式化、校验与实体类转换工具


十七、常见错误四:Cannot deserialize value of type

报错示例:

Cannot deserialize value of type `java.lang.Integer` from String "abc"

一般意思是:

前端传的字段类型,和 Java 接收类型不匹配。

例如后端:

private Integer age;

前端却传:

{
  "age": "abc"
}

这肯定转不了。

正确:

{
  "age": 18
}

再比如后端用对象接:

@RequestBody UserCreateRequest request

前端却传数组:

[
  {
    "username": "zhangsan"
  }
]

这也不匹配。


十八、常见错误五:对象里全是 null

接口没有报错,但 DTO 里的字段都是 null

常见原因:

1. JSON 字段名和 Java 字段名不一致
2. 没有 getter / setter
3. Lombok 没生效
4. 前端传的是嵌套对象,后端用平铺字段接
5. 请求体实际不是 JSON

比如前端:

{
  "user_name": "zhangsan"
}

后端:

private String userName;

如果没有配置命名策略,也没有 @JsonProperty,就可能接不到。

可以这样写:

@JsonProperty("user_name")
private String userName;

十九、DTO 不要直接用 Entity

有些项目喜欢这样写:

@PostMapping("/users")
public String createUser(@RequestBody UserEntity entity) {
    return "ok";
}

不建议。

更推荐:

Request DTO:接收前端参数
Entity / DO:对应数据库表
Response VO:返回给前端

例如:

@Data
public class UserCreateRequest {

    private String username;

    private String nickname;

    private Integer age;
}

Entity 可能有这些字段:

id
password
deleted
createdAt
updatedAt
createdBy
version
internalStatus

这些字段不一定应该让前端传。
所以接口入参用 DTO,会更安全、更清楚。


二十、推荐的接口写法

一个比较舒服的写法是:

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public ApiResult<Long> createUser(@Valid @RequestBody UserCreateRequest request) {
        // 这里调用 service 保存用户
        return ApiResult.success(1001L);
    }
}

DTO:

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

@Data
public class UserCreateRequest {

    @NotBlank(message = "用户名不能为空")
    private String username;

    private String nickname;

    @NotNull(message = "年龄不能为空")
    private Integer age;
}

返回对象示例:

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class ApiResult<T> {

    private Integer code;

    private String message;

    private T data;

    public static <T> ApiResult<T> success(T data) {
        return new ApiResult<>(200, "success", data);
    }
}

二十一、排查清单

如果 Spring Boot 接收 JSON 参数有问题,按这个顺序查:

1. 请求方法是不是 POST / PUT / PATCH
2. 请求头是不是 Content-Type: application/json
3. 请求体里是否真的有 JSON
4. JSON 格式是否合法
5. 最外层是对象还是数组
6. Java 接收类型是否匹配
7. 字段名是否一致
8. 字段类型是否一致
9. DTO 是否有 getter / setter
10. Lombok 是否生效
11. LocalDateTime 是否配置格式
12. 是否误用了 @RequestParam
13. 是否需要 @RequestPart
14. 是否有全局异常处理吞掉了真实报错
15. 是否使用了正确的 Spring Boot 和 Jackson 依赖

二十二、常见问题 FAQ

1. @RequestBody 是干什么的?

@RequestBody 用来读取 HTTP 请求体,并把请求体内容转换成 Java 对象。Spring MVC 会通过 HttpMessageConverter 完成这个转换。(Home)


2. JSON 参数必须加 @RequestBody 吗?

如果你想从请求体中读取 JSON,一般要加。

public String create(@RequestBody UserCreateRequest request)

如果是 URL 查询参数或表单参数,一般用 @RequestParam


3. @RequestBody 可以接收 GET 请求吗?

不建议这么做。

GET 请求通常用查询参数:

/users?id=1

后端用:

@GetMapping("/users")
public String getUser(@RequestParam Long id) {
    return "ok";
}

JSON 请求体更适合 POST、PUT、PATCH。


4. 为什么前端传了 JSON,后端接不到?

优先检查:

Content-Type 是否是 application/json
JSON 格式是否合法
字段名是否一致
Controller 是否写了 @RequestBody
请求体是否真的发出去了

5. JSON 数组怎么接收?

List<T>

@PostMapping("/batch")
public String batch(@RequestBody List<UserCreateRequest> users) {
    return "ok";
}

6. 表单提交能用 @RequestBody 吗?

不建议。

表单参数用 @RequestParam 更合适。Spring 官方文档也提醒,form data 应该用 @RequestParam 读取,而不是依赖 @RequestBody。(Home)


7. 文件上传加 JSON 怎么接?

multipart/form-data,后端用 @RequestPart

@PostMapping("/upload")
public String upload(@RequestPart("file") MultipartFile file,
                     @RequestPart("meta") FileMetaRequest meta) {
    return "ok";
}

8. LocalDateTime 接收失败怎么办?

字段上加:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createdAt;

同时要求前端传统一格式:

{
  "createdAt": "2026-07-29 10:30:00"
}

二十三、最后总结

Spring Boot 接收 JSON 参数,核心就是三件事:

请求头对不对
JSON结构对不对
Java接收类型对不对

最常见写法:

@PostMapping("/users")
public String createUser(@RequestBody UserCreateRequest request) {
    return "ok";
}

请求头:

Content-Type: application/json

请求体:

{
  "username": "zhangsan",
  "age": 18
}

不要把所有参数都塞进 Map,也不要直接用 Entity 接收前端参数。
正式业务接口建议用 Request DTO,字段清楚,后期更好维护。


二十四、相关文章

JSON 转 Java 实体类:

JSON转Java实体类完整教程

Spring Boot DataSource 报错:

Spring Boot启动报错 Failed to configure a DataSource 解决办法

Spring Boot 与 JDK 兼容:

Spring Boot与JDK版本兼容表

Spring Boot 2 升级 3:

Spring Boot 2升级到Spring Boot 3完整指南

Java 开发环境配置:

Java开发环境配置专题

Json 工具:

Json哥 - JSON 在线解析、格式化、校验与实体类转换工具


更新记录

2026-07-29:
- 创建 Spring Boot 接收 JSON 参数教程
- 增加对象、数组、嵌套对象、List、Map、JsonNode 接收示例
- 增加 LocalDateTime、@JsonProperty、@Valid 参数校验示例
- 增加 @RequestBody、@RequestParam、@RequestPart 区别说明
- 增加 Required request body is missing、415、JSON parse error 排查