@RequestBody的伪表单提交场景

@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 无法自动完成这个绑定)。

相关推荐
栀秋6662 小时前
防抖 vs 节流:从百度搜索到京东电商,看前端性能优化的“节奏哲学”
前端·javascript
一颗烂土豆2 小时前
vfit.js v2.0.0 发布:精简、语义化与核心重构 🎉
前端·vue.js·响应式设计
有意义2 小时前
深入防抖与节流:从闭包原理到性能优化实战
前端·javascript·面试
气π2 小时前
【JavaWeb】——(若依+AI)-帝可得实践项目
java·spring
王中阳Go2 小时前
手把手教你用 Go + Eino 搭建一个企业级 RAG 知识库(含代码与踩坑)
人工智能·后端·go
幺零九零零2 小时前
Golang-Swagger
开发语言·后端·golang
可观测性用观测云3 小时前
网站/接口可用性拨测最佳实践
前端
乌暮3 小时前
JavaEE初阶--多线程案例
java·java-ee
小光学长3 小时前
基于ssm旅游管理系统的开发与设计z050cft7(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·旅游