【Spring Boot + MyBatis|第2篇】@RequestParam、@PathVariable、@RequestBody 区别详解

发布时间:2026/6/9 3:39:20

【Spring Boot + MyBatis|第2篇】@RequestParam、@PathVariable、@RequestBody 区别详解 前言上一篇我们学习了 Spring Boot MyBatis 项目中的三层架构也就是 Controller、Service、Mapper 分别负责什么。在实际写 Controller 接口的时候还有一个很常见的问题前端传过来的参数后端到底应该怎么接收比如有时候我们这样写GetMapping(/{id})publicResultgetById(PathVariableIntegerid){returnResult.success();}有时候又这样写GetMappingpublicResultlist(RequestParamStringname){returnResult.success();}新增数据时又经常这样写PostMappingpublicResultsave(RequestBodyEmpemp){returnResult.success();}这里就涉及到三个非常常见的注解RequestParamPathVariableRequestBody这一篇我们就来单独梳理一下它们的区别以及在项目中分别适合用在哪些场景。一、三个注解分别用来接收什么可以先用一句话简单区分RequestParam接收请求参数PathVariable接收路径参数RequestBody接收请求体 JSON 数据。注解主要接收位置常见请求方式典型场景RequestParamURL 问号后面的参数GET条件查询、分页查询PathVariableURL 路径中的参数GET、DELETE根据 id 查询、删除RequestBody请求体中的 JSONPOST、PUT新增、修改复杂对象下面我们分别来看。二、PathVariable接收路径参数1. 功能需求比如我们要根据员工 id 查询员工信息。前端请求路径是GET /emps/1这里的1不是普通参数而是路径的一部分。这种情况下就适合使用PathVariable。2. Controller 层实现RestControllerRequestMapping(/emps)publicclassEmpController{AutowiredprivateEmpServiceempService;GetMapping(/{id})publicResultgetById(PathVariableIntegerid){EmpempempService.getById(id);returnResult.success(emp);}}3. Service 层实现publicinterfaceEmpService{EmpgetById(Integerid);}ServicepublicclassEmpServiceImplimplementsEmpService{AutowiredprivateEmpMapperempMapper;OverridepublicEmpgetById(Integerid){returnempMapper.getById(id);}}4. Mapper 层实现MapperpublicinterfaceEmpMapper{Select(select id, username, name, gender, phone from emp where id #{id})EmpgetById(Integerid);}5. 文字说明这个接口的完整访问路径是/emps/{id}当请求路径是/emps/1Spring 会把路径中的1取出来赋值给 Controller 方法中的id参数。所以PathVariableIntegerid表示从路径中获取参数。这种写法一般用于“资源定位”比如GET /emps/1 根据 id 查询员工 DELETE /emps/1 根据 id 删除员工 GET /depts/3 根据 id 查询部门6. 涉及知识点1. 路径变量名称一致时可以省略名称如果路径中写的是GetMapping(/{id})方法参数也叫idPathVariableIntegerid那么可以直接这样写。2. 路径变量名称不一致时要指定名称如果路径中写的是GetMapping(/{empId})但是方法参数叫id就需要这样写GetMapping(/{empId})publicResultgetById(PathVariable(empId)Integerid){returnResult.success(empService.getById(id));}否则 Spring 可能不知道应该把哪个路径变量赋值给哪个参数。三、RequestParam接收普通请求参数1. 功能需求比如我们要做员工条件查询前端可能会这样传参GET /emps?name张三gender1page1pageSize10这里的参数都在 URL 的问号后面这种参数就适合使用RequestParam。2. Controller 层实现RestControllerRequestMapping(/emps)publicclassEmpController{AutowiredprivateEmpServiceempService;GetMappingpublicResultpage(RequestParam(defaultValue1)Integerpage,RequestParam(defaultValue10)IntegerpageSize,Stringname,Integergender){PageBeanpageBeanempService.page(page,pageSize,name,gender);returnResult.success(pageBean);}}3. Service 层实现publicinterfaceEmpService{PageBeanpage(Integerpage,IntegerpageSize,Stringname,Integergender);}ServicepublicclassEmpServiceImplimplementsEmpService{AutowiredprivateEmpMapperempMapper;OverridepublicPageBeanpage(Integerpage,IntegerpageSize,Stringname,Integergender){PageHelper.startPage(page,pageSize);ListEmpempListempMapper.list(name,gender);PageEmpp(PageEmp)empList;returnnewPageBean(p.getTotal(),p.getResult());}}4. Mapper 层实现MapperpublicinterfaceEmpMapper{ListEmplist(Stringname,Integergender);}如果使用 XML 写动态 SQL可以这样写selectidlistresultTypecom.example.pojo.Empselect id, username, name, gender, phone from empwhereiftestname ! null and name ! name like concat(%, #{name}, %)/ififtestgender ! nulland gender #{gender}/if/whereorder by update_time desc/select5. 文字说明RequestParam主要用来接收普通请求参数。比如请求地址是/emps?page1pageSize10name张三gender1那么后端可以接收到page1pageSize10name张三gender1这里有一个细节RequestParam(defaultValue1)Integerpage表示如果前端没有传page默认值就是1。RequestParam(defaultValue10)IntegerpageSize表示如果前端没有传pageSize默认每页查询 10 条。6. 涉及知识点1. RequestParam 默认 required 为 true如果这样写RequestParamStringname默认表示name参数必须传。如果前端没有传可能会报错。如果这个参数不是必须的可以这样写RequestParam(requiredfalse)Stringname或者直接省略注解Stringname在 Spring Boot 中简单参数很多时候可以不写RequestParam也能正常接收。2. defaultValue 可以设置默认值分页查询中经常这样写RequestParam(defaultValue1)Integerpage,RequestParam(defaultValue10)IntegerpageSize这样前端即使不传分页参数后端也能有默认值不容易出现空指针问题。3. 适合接收简单参数RequestParam适合接收StringIntegerLongDoubleBoolean简单日期参数比如/emps?name张三 /emps?page1pageSize10 /emps?gender1这些都比较适合使用RequestParam。四、RequestBody接收 JSON 请求体1. 功能需求如果我们要新增一个员工前端一般不会把所有字段都拼到 URL 后面而是通过 JSON 传给后端。请求方式一般是POST /emps请求体内容类似{username:zhangsan,name:张三,gender:1,phone:13800000000}这种情况下就适合使用RequestBody。2. Controller 层实现RestControllerRequestMapping(/emps)publicclassEmpController{AutowiredprivateEmpServiceempService;PostMappingpublicResultsave(RequestBodyEmpemp){empService.save(emp);returnResult.success();}}3. Service 层实现publicinterfaceEmpService{voidsave(Empemp);}ServicepublicclassEmpServiceImplimplementsEmpService{AutowiredprivateEmpMapperempMapper;Overridepublicvoidsave(Empemp){emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());empMapper.insert(emp);}}4. Mapper 层实现MapperpublicinterfaceEmpMapper{Insert(insert into emp(username, name, gender, phone, create_time, update_time) values(#{username}, #{name}, #{gender}, #{phone}, #{createTime}, #{updateTime}))voidinsert(Empemp);}5. 文字说明RequestBody表示从请求体中获取 JSON 数据并把 JSON 转换成 Java 对象。前端传来的 JSON 是{username:zhangsan,name:张三,gender:1,phone:13800000000}后端接收时写RequestBodyEmpempSpring Boot 会自动把 JSON 中的字段封装到Emp对象中。也就是说JSON 字段 username - Emp 对象的 username 属性 JSON 字段 name - Emp 对象的 name 属性 JSON 字段 gender - Emp 对象的 gender 属性 JSON 字段 phone - Emp 对象的 phone 属性这种方式非常适合新增和修改操作。6. 涉及知识点1. 前端需要设置 Content-Type使用RequestBody接收 JSON 时前端请求头一般要设置Content-Type: application/json如果前端传的是表单格式而后端使用RequestBody就可能接收不到数据。2. JSON 字段名要和 Java 属性名对应比如 Java 类中属性叫privateStringusername;那么 JSON 中最好也写{username:zhangsan}如果字段名对不上就可能出现属性为null的情况。3. RequestBody 适合复杂对象比如新增员工、修改员工、提交订单、保存表单等场景一般都会使用RequestBody。因为这些数据字段较多用 JSON 表达更清楚。五、三个注解的使用场景对比1. 根据 id 查询请求路径GET /emps/1后端写法GetMapping(/{id})publicResultgetById(PathVariableIntegerid){returnResult.success(empService.getById(id));}适合使用PathVariable2. 条件分页查询请求路径GET /emps?page1pageSize10name张三gender1后端写法GetMappingpublicResultpage(RequestParam(defaultValue1)Integerpage,RequestParam(defaultValue10)IntegerpageSize,Stringname,Integergender){returnResult.success(empService.page(page,pageSize,name,gender));}适合使用RequestParam3. 新增员工请求路径POST /emps请求体{username:zhangsan,name:张三,gender:1,phone:13800000000}后端写法PostMappingpublicResultsave(RequestBodyEmpemp){empService.save(emp);returnResult.success();}适合使用RequestBody六、最容易混淆的地方1. RequestParam 和 PathVariable 的区别看参数在什么位置。如果参数在问号后面/emps?id1一般使用RequestParamIntegerid如果参数在路径中/emps/1一般使用PathVariableIntegerid2. RequestParam 和 RequestBody 的区别看前端传的是普通参数还是 JSON。如果是这样/emps?name张三gender1使用RequestParam如果是这样{name:张三,gender:1}使用RequestBody3. GET 请求一般不使用 RequestBody虽然有些情况下 GET 请求也能带请求体但是在实际项目中不推荐这样做。一般约定是GET 查询数据 POST 新增数据 PUT 修改数据 DELETE 删除数据所以查询参数通常放在 URL 中新增和修改数据通常放在请求体中。七、常见报错和解决方式1. Required request parameter is not present这个错误通常是因为使用了RequestParamStringname但是前端没有传name参数。解决方式RequestParam(requiredfalse)Stringname或者设置默认值RequestParam(defaultValue)Stringname2. JSON 数据接收后属性都是 null常见原因有两个。第一个原因是前端没有设置Content-Type: application/json第二个原因是 JSON 字段名和 Java 对象属性名不一致。比如 Java 属性是privateStringusername;但是前端传的是{user_name:zhangsan}这样就可能无法自动封装。3. PathVariable 接收不到参数如果路径中变量名和方法参数名不一致需要手动指定GetMapping(/{empId})publicResultgetById(PathVariable(empId)Integerid){returnResult.success(empService.getById(id));}八、实际开发中的使用建议在项目中可以按照下面的习惯来选择场景推荐写法根据 id 查询PathVariable根据 id 删除PathVariable条件查询RequestParam分页查询RequestParam新增对象RequestBody修改对象RequestBody批量删除 id 数组RequestParam或RequestBody看前端传参方式简单记忆路径中的参数用PathVariable问号后面的参数用RequestParamJSON 请求体用RequestBody。九、总结这一篇主要学习了 Spring Boot 中 Controller 接收参数的三种常见方式。PathVariable主要用来接收路径参数适合根据 id 查询、根据 id 删除这类接口。RequestParam主要用来接收 URL 问号后面的普通参数适合条件查询、分页查询。RequestBody主要用来接收请求体中的 JSON 数据适合新增和修改复杂对象。这三个注解在项目开发中非常常见尤其是写增删改查接口时基本都会用到。只要能判断清楚“参数在哪里”基本就能选对注解。下一篇我们可以继续学习项目中很常见的统一返回结果类也就是Result类应该怎么设计以及为什么不建议接口直接返回普通字符串或实体对象。

相关新闻