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

相关推荐
monkeyhlj1 小时前
Harness理解学习
java·人工智能·python·学习·ai编程
Oneslide1 小时前
银河麒麟紧Linux 内核CopyFail高危漏洞(CVE-2026-31431)通告
后端
RelishCoding1 小时前
SpringMVC-01
后端·spring
lichenyang4531 小时前
从 AI 聊天组件源码复盘工程化架构:MVVM、解耦、Provider 与 SSE 流式响应
前端
Maimai108081 小时前
TanStack Table 入门:为什么它是 React 表格开发里的“表格引擎”
前端·javascript·react.js·架构·前端框架·reactjs
踩着两条虫1 小时前
VTJ.PRO 开源 AI 低代码引擎深度评测大纲
前端·低代码·开源软件
未若君雅裁2 小时前
Spring Cloud 组件全景与注册中心原理
后端·spring·spring cloud
西凉的悲伤2 小时前
SpringBoot WebClient 介绍
java·spring boot·后端·webclient
Simon523142 小时前
mybatis执行流程、关联映射、注解开发
java·开发语言·mybatis
神奇小汤圆2 小时前
从零搭建量化投资系统:用 Qlib 一行代码搞定均线分析
后端