@RequestBody
@RequestBody是什么?
@RequestBody是 Spring MVC/Spring Boot 中处理 HTTP 请求的核心注解,它的核心作用是:将 HTTP 请求体中的数据(通常是 JSON/XML 格式)自动解析并绑定到方法的参数对象上。
Spring MVC 框架默认参数解析行为:
在默认配置下,Spring MVC 的请求参数解析器(HandlerMethodArgumentResolver)仅会解析符合 application/x-www-form-urlencoded 媒体类型的请求参数(如 URL 路径参数、查询字符串参数、表单提交的键值对参数),不会主动读取 HTTP 请求体的 JSON 数据,也不支持将请求体中的 JSON 格式数据自动绑定到控制器方法的参数对象上。
@RequestBody 注解用于声明控制器方法的参数需从 HTTP 请求体中获取数据,并触发 Spring MVC 的消息转换器(HttpMessageConverter)完成以下核心操作:
读取请求体中的原始字符数据(通常为 JSON 格式);
根据参数声明的 Java 类型,将 JSON 数据反序列化为对应的 Java 对象;
将反序列化后的 Java 对象注入到控制器方法的参数中,完成参数绑定。
简言之,该注解覆盖了 Spring MVC 对参数来源的默认规则,明确指定参数数据来源为 HTTP 请求体,并自动完成 JSON 到 Java 对象的转换,把字段值一一赋值进去。
是否需要加@RequestBody
| 场景 | 是否需要加 @RequestBody | 备注 |
|---|---|---|
| POST/PUT 请求 | ✅ 必须加 | 参数是 JSON 格式(放在请求体) |
| GET 请求 | ❌ 不能加 | GET 请求没有请求体,参数只能在 URL 中 |
| 表单提交 | ❌ 不加 | 用 @RequestParam 或直接绑定对象即可 |
| 参数是 XML 格式 | ✅ 必须加 | 解析请求体中的 XML 数据 |
场景 1:传统表单提交(最常见,无需加 @RequestBody)
这是原生 <form> 标签或前端模拟的表单提交,特征是:
请求头 Content-Type: application/x-www-form-urlencoded;
参数以 key1=value1&key2=value2 的形式放在请求体。
场景 2:带文件的表单提交(multipart/form-data,无需加 @RequestBody)
如果表单包含文件上传,请求头是 Content-Type: multipart/form-data,同样不需要加 @RequestBody,需用 @RequestParam/@RequestPart 接收
场景 3:伪表单提交(实际是 JSON,需要加 @RequestBody)
有些前端会用表单提交的写法,但实际传的是 JSON 数据:
请求头 Content-Type: application/json;
参数是 JSON 字符串(而非 key=value),放在请求体中。
这种场景本质是JSON 请求体提交,只是前端写法像表单,必须加 @RequestBody
伪表单提交举例
前端用表单提交的写法,但实际传的是 JSON 数据,伪表单提交举例如下
表单提交的代码:视觉和交互层面,用了 Element UI 的 <el-form> 组件,搭配 <el-form-item>、<el-input>、<el-radio-group> 等表单类组件,呈现出表单填写的形态,符合前端开发中表单写法的认知。
javascript
<el-form ref="dataForm" :model="dataForm" :rules="dataRule" label-width="100px" style="width:95%;" @keyup.enter.native="dataFormSubmit()">
<el-row>
<el-col :span="24">
<el-form-item label="摄像头名称" prop="cameraName">
<el-input v-model="dataForm.cameraName" placeholder="请输入摄像头名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="IP地址" prop="macID">
<el-input v-model="dataForm.macID" placeholder="请输入IP地址(必须唯一)" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="方向" prop="direction">
<el-radio-group v-model="dataForm.direction">
<el-radio :label="1">进场</el-radio>
<el-radio :label="2">出场</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="状态" prop="state">
<el-radio-group v-model="dataForm.state">
<el-radio :label="1">启用</el-radio>
<el-radio :label="2">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="排序" prop="seq">
<el-input v-model="dataForm.seq" placeholder="请输入排序号" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="danger" @click="visible=false">取 消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确 定</el-button>
</span>
表单与 dataForm 对象做双向绑定,每个表单项都直接关联 dataForm 的子字段,里面包含了摄像头的所有信息:
javascript
dataForm: {
cameraInfoId: '',
communityId: '',
cameraName: '',
macID: '',
direction: '1',
seq: '1',
state: 1,
remark: ''
}
这是多个字段组成的复杂数据,不是单个的键值对,而是一个完整的对象
再看前端的数据提交时把完整对象传给后端,点击确定按钮触发的自定义的 dataFormSubmit() 方法,触发异步请求:
javascript
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const param = {
'cameraInfoId': this.dataForm.cameraInfoId,
'communityId': this.dataForm.communityId,
'cameraName': this.dataForm.cameraName,
'macID': this.dataForm.macID,
'direction': this.dataForm.direction,
'seq': this.dataForm.seq,
'state': this.dataForm.state,
'remark': this.dataForm.remark
}
if (!this.dataForm.cameraInfoId) {
add(param).then(res => {
if (res.code === 200) {
this.visible = false
this.$emit('refreshDataList')
this.$message.success(res.msg)
} else {
this.$message.error(res.msg)
}
})
} else {
edit(param).then(res => {
if (res.code === 200) {
this.visible = false
this.$emit('refreshDataList')
this.$message.success(res.msg)
} else {
this.$message.error(res.msg)
}
})
}
}
})
export function add(data) {
return request({
url: '/sys/camera/add',
method: 'post',
data
})
}
export function edit(data) {
return request({
url: '/sys/camera/edit',
method: 'put',
data
})
}
先校验表单,通过后把 dataForm 的所有字段组装成param对象(和dataForm内容一致);
判断是新增 / 编辑:
新增:调用add(param),对应 POST 请求,把param作为data传给后端;
编辑:调用edit(param),对应 PUT 请求,同样把param作为data传给后端
前端向后端发新增的 HTTP 请求的过程
请求 URL为:/sys/camera/add
Request Body 请求体为:传输的是 JSON 数据
javascript
{
"cameraInfoId": 0,
"communityId": "19",
"cameraName": "1号摄像头",
"macID": "127.0.0.1",
"direction": 2,
"seq": "2",
"state": 1,
"remark": "高清摄像头"
}
Content-Type 请求头:Content-Type: application/json
其中 application/json 是 HTTP 标准定义的媒体类型,专门表示请求体里的数据是 JSON 格式的字符串
前端的 request 库(本质是 axios)会把 data 参数(也就是param对象)这个 JavaScript 对象转换成 JSON 字符串,再把这个 JSON 字符串放在 HTTP 请求的请求体(Request Body)里(而不是 URL 里),且自动添加 Content-Type: application/json 请求头。
后端接收到请求后,先读取 Content-Type 再决定用哪种解析规则处理请求体,当看到请求头时 Content-Type: application/json,Spring 会用JSON 解析器把请求体的 JSON 字符串解析成 Java 对象。
不管是新增(POST)还是编辑(PUT)都要传递多个字段的完整摄像头对象,这些数据量大且结构复杂,不可能拼在 URL 里(URL 长度有限、不安全、格式不支持),前端把完整对象通过data参数放在请求体里传输,后端读取请求体里的 JSON,因此必须加@RequestBody。
总结:Spring MVC 中,默认情况下参数绑定是从 URL 或表单(x-www-form-urlencoded)中解析,只有通过 @RequestBody 注解才能让 Spring 主动解析请求体中的 JSON 字符串,并映射到入参的 Java 实体类(若不使用该注解,Spring 无法自动完成这个绑定)。