惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
WordPress大学
WordPress大学
爱范儿
爱范儿
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
博客园_首页
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
MyScale Blog
MyScale Blog
IT之家
IT之家
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
Microsoft Security Blog
Microsoft Security Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
人人都是产品经理
人人都是产品经理

沉迷在 - Java.li - 无法自拔

Spring Boot 整合 MyBatis DeepSeek Harness 深入解读 Spring Boot 连接 MySQL GitHub发生故障,崩了7个小时 Spring Boot 配置文件怎么写 SpringBoot 教程 MyBatis Invalid bound statement 怎么处理 Spring Boot 端口被占用怎么处理 Qdrant 向量数据库入门 Spring Boot启动报错 Failed to configure a DataSource 解决办法 Maven依赖冲突怎么排查?dependency:tree、exclusions与版本统一完整教程 - 沉迷在 - Java.li - 无法自拔 给网站加一条会动的人群横幅:Little People 动画组件 Demo - 沉迷在 - Java.li - 无法自拔 【soso.re】聚合热榜 | CloudflareWorkers部分接口失效的排查与代理方案 - 沉迷在 - Java.li - 无法自拔 Spring Boot 2升级到Spring Boot 3完整指南:JDK17、javax迁移jakarta、依赖兼容与常见报错 - 沉迷在 - Java.li - 无法自拔 随机小姐姐视频 - 沉迷在 - Java.li - 无法自拔 Spring Boot与JDK版本兼容表:Spring Boot 2.x / 3.x / 4.x应该用哪个Java版本? - 沉迷在 - Java.li - 无法自拔 No compiler is provided in this environment解决办法:JDK、JRE、Maven与IDEA排查 - 沉迷在 - Java.li - 无法自拔 置身钉内|含全文 PDF - 沉迷在 - Java.li - 无法自拔 javac不是内部或外部命令怎么解决?JDK、JAVA_HOME和Path完整排查 Windows配置JAVA_HOME后不生效怎么办?java -version显示旧版本解决办法 Maven Could not transfer artifact 下载失败解决:settings.xml、国内镜像、本地缓存与代理排查 JSON转Java实体类完整教程:对象、数组、嵌套结构与LocalDateTime处理 Windows安装JDK 8 / 17 / 21 / 25完整教程:JAVA_HOME环境变量配置与验证 IDEA下载JDK很慢怎么办?手动配置本地JDK完整教程 Java版本号与class文件major version对照表:Unsupported class file major version 52 / 55 / 61 / 65 / 69 / 70 Gradle国内镜像配置教程:init.gradle、repositories与Wrapper加速完整指南 Maven国内镜像settings.xml配置大全:阿里云、腾讯云、华为云、清华源 Java开发者必备工具箱 2026年Java行情深度解读:就业真实现状、语言排名与开发者破局指南 如何防止服务器被暴力破解?2026年最全的5层防护实战指南
Spring Boot 接收 JSON 参数
HiF · 2026-07-29 · via 沉迷在 - Java.li - 无法自拔

最后更新: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

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

比如后端是:

@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 排查