RequestBody
本章是请求参数获取系列的核心章节。如果说
@RequestParam、@RequestHeader、@CookieValue处理的是"键值对形式"的零散数据,那么@RequestBody处理的是"结构化体"——它将 HTTP 请求体中的 JSON 或 XML 反序列化为完整的 Java 对象。这是 RESTful API 开发中最常用的数据绑定方式,也是前后端分离架构的基石。
定义与作用
@RequestBody 用于将 HTTP 请求体(Request Body)中的数据反序列化为 Java 对象。
在 RESTful 架构中,客户端通常以 JSON 格式提交复杂数据:
{
"name": "张三",
"department": "技术部",
"salary": 25000,
"skills": ["Java", "Spring", "MySQL"]
}
@RequestBody 的职责是:读取请求体中的这段 JSON 文本,通过 HttpMessageConverter 转换为对应的 Java 对象(如 Employee 实例)。
生活类比:拆箱验货
想象飞翔科技采购部收到一个快递包裹:
- 包裹外箱(HTTP 请求头):贴有
Content-Type: application/json标签,说明里面是 JSON 格式的货物清单 - 包裹内物(HTTP 请求体):一张详细的 JSON 货物清单
- @RequestBody:仓库管理员根据外箱标签确认格式(JSON),打开包裹,把清单上的每一项对应到仓库货架的指定位置(Java 对象的字段),完成入库
如果外箱标签写的是 application/xml,管理员就会按 XML 格式解析。如果标签缺失或写错,管理员会拒收(返回 415 Unsupported Media Type)。
核心原理
请求体反序列化完整流程
关键组件说明:
- RequestResponseBodyMethodProcessor:处理
@RequestBody和@ResponseBody参数的解析器 - HttpMessageConverter:消息转换器,负责 HTTP 请求体/响应体与 Java 对象之间的互相转换
- MappingJackson2HttpMessageConverter:默认的 JSON 转换器,依赖 Jackson 库完成 JSON ↔ Java 对象的序列化/反序列化
Content-Type 与 Converter 的匹配
重要边界:@RequestBody 要求请求必须携带 Content-Type 头,且该类型必须被注册的 HttpMessageConverter 支持。Spring Boot 默认注册了 JSON、XML、String 等常用转换器。
适用位置与常用属性
@RequestBody 只能标注在 Controller 方法的参数 上。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
required | boolean | true | 请求体是否必须存在。false 时请求体为空则参数为 null |
注意:@RequestBody 没有 name 和 defaultValue 属性,因为它绑定的是整个请求体,不是某个键值对。
完整示例
场景
飞翔科技员工管理系统的前端黄俪开发了一个新增员工的表单页面。用户填写信息后,前端以 JSON 格式提交到后端。后端小崔使用 @RequestBody 接收并保存员工数据。
代码实现
package com.feixiang.web;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/employees")
public class EmployeeController {
/**
* 新增员工:JSON → Java 对象
*/
@PostMapping
public String createEmployee(@RequestBody Employee employee) {
return String.format("已创建员工: %s, 部门: %s, 薪资: %d",
employee.getName(), employee.getDepartment(), employee.getSalary());
}
/**
* 批量更新:JSON 数组 → List<Java 对象>
*/
@PutMapping("/batch")
public String batchUpdate(@RequestBody List<Employee> employees) {
return "批量更新员工数量: " + employees.size();
}
/**
* 可选请求体
*/
@PostMapping("/quick-create")
public String quickCreate(@RequestBody(required = false) Employee employee) {
if (employee == null) {
return "使用默认配置创建员工";
}
return "快速创建: " + employee.getName();
}
}
// Employee.java
package com.feixiang.web;
import java.util.List;
public class Employee {
private String name;
private String department;
private Integer salary;
private List<String> skills;
// Getter / Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public Integer getSalary() { return salary; }
public void setSalary(Integer salary) { this.salary = salary; }
public List<String> getSkills() { return skills; }
public void setSkills(List<String> skills) { this.skills = skills; }
}
HTTP 请求示例
示例 1:JSON → Java 对象
curl -X POST "http://localhost:8080/employees" \
-H "Content-Type: application/json" \
-d '{
"name": "张三",
"department": "技术部",
"salary": 25000,
"skills": ["Java", "Spring", "MySQL"]
}'
响应:
已创建员工: 张三, 部门: 技术部, 薪资: 25000
示例 2:JSON 数组 → List
curl -X PUT "http://localhost:8080/employees/batch" \
-H "Content-Type: application/json" \
-d '[
{"name": "张三", "department": "技术部", "salary": 25000},
{"name": "李四", "department": "产品部", "salary": 22000}
]'
响应:
批量更新员工数量: 2
示例 3:缺少 Content-Type(报错)
curl -X POST "http://localhost:8080/employees" \
-d '{"name": "张三"}'
响应(HTTP 415):
{
"timestamp": "2024-01-15T11:20:33.789+00:00",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported"
}
未指定 Content-Type 时,curl 默认使用 application/x-www-form-urlencoded,Spring MVC 找不到支持该类型的 @RequestBody Converter,返回 415。
易错场景与面试考点
误区一:JSON 字段名与 Java 属性名不匹配
现象:客户端传 {"userName": "张三"},Java 对象是 private String name,绑定后 name 为 null。
原因:Jackson 默认按 Java 属性名(name)匹配 JSON 键(userName),不匹配则跳过。
解决:
- 统一命名规范(推荐):前后端约定统一使用驼峰命名
- 使用
@JsonProperty注解映射:
@JsonProperty("userName")
private String name;
误区二:日期格式解析失败
现象:{"hireDate": "2024-01-15"} 绑定到 LocalDate hireDate 报错。
原因:Jackson 默认不支持 yyyy-MM-dd 格式的日期解析。
解决:
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate hireDate;
或在全局配置 ObjectMapper:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
误区三:400 错误排查清单
当 @RequestBody 接口返回 HTTP 400 时,按以下顺序排查:
| 排查项 | 检查方法 | 常见错误 |
|---|---|---|
| Content-Type | 确认请求头包含 Content-Type: application/json | 缺失或写错为 text/plain |
| JSON 格式 | 用 JSON 校验工具检查 | 末尾多余逗号、引号不匹配 |
| 字段类型 | 确认 JSON 值类型与 Java 类型匹配 | 字符串传 "25" 但 Java 是 int |
| 必填字段 | 检查 Java 对象是否有无参构造 | 缺少默认构造器导致 Jackson 无法实例化 |
| 日期格式 | 检查日期字符串格式 | 2024-01-15 与 2024/01/15 不匹配 |
面试高频:@RequestBody 和 @RequestParam 的区别
标准回答:
- 数据来源:
@RequestParam从 URL 查询参数或表单数据获取,@RequestBody从 HTTP 请求体获取 - 数据格式:
@RequestParam处理键值对(name=value),@RequestBody处理结构化数据(JSON/XML) - Content-Type:
@RequestBody必须配合Content-Type: application/json(或其他支持的类型),@RequestParam通常配合application/x-www-form-urlencoded - 使用场景:
@RequestParam适合简单参数、GET 请求;@RequestBody适合复杂对象、POST/PUT 请求 - 底层机制:
@RequestBody依赖HttpMessageConverter进行反序列化,@RequestParam依赖PropertyEditor进行类型转换
小结
@RequestBody 是 Spring MVC 处理 RESTful API 请求体的核心注解。它通过 HttpMessageConverter 将 JSON/XML 等格式的请求体反序列化为 Java 对象,是前后端分离架构中数据传输的桥梁。
核心要点:
- 必须携带正确的
Content-Type请求头(通常是application/json) - 依赖 Jackson 库完成 JSON 反序列化,确保 Java 对象有无参构造器和 Getter/Setter
- JSON 字段名默认与 Java 属性名匹配,不匹配时用
@JsonProperty映射 - 400 错误优先排查 Content-Type、JSON 格式、类型匹配
本章与全局的关系:本章讲解了"如何将请求体中的结构化数据映射为 Java 对象"。下一节 @ModelAttribute 将讲解另一种对象绑定方式——它专门处理表单数据的绑定,与 @RequestBody 形成互补。