SpringBoot 接口传参RequestParam、RequestBody、PathVariable 怎么选目录一、RequestParam从 URL 查询参数中取值二、PathVariable从 URL 路径中取值三、RequestBody从请求体中取值三种注解对比什么时候用哪个按 HTTP 语义来选使用时注意事项小结我们在学习后端开发的时候都在相应教学内容上看到过这三个注解。教程里直接给使用方式但却没有详细说什么时候该用哪个。等到真正写项目接口的时候就会开始纠结分页参数用RequestParam那查详情的 ID 放在路径里用PathVariable还是也用RequestParam提交表单十几个字段是全用RequestParam一个个接还是塞进一个对象用RequestBody三种注解都能拿到前端传来的数据但它们的数据来源、适用场景、HTTP 语义完全不同。搞混了代码能跑但接口设计会很别扭。我们一个一个来看一下使用场景。一、RequestParam从 URL 查询参数中取值RequestParam对应的是 URL 中?后面的查询参数Query String。GetMapping(/users)publicListUserlistUsers(RequestParam(defaultValue1)intpage,RequestParam(defaultValue10)intsize){returnuserService.listUsers(page,size);}前端请求GET /users?page1size10Spring 看到RequestParam就知道去 URL 的查询参数里找page和size这两个 key取出来转成int注入到方法参数里。defaultValue是个保底如果前端没传这个参数就用默认值不会报错。适用场景筛选、排序、分页这种可选的查询条件。用RequestParam最合适。参数是 URL 的一部分浏览器地址栏能直接看到方便调试和分享链接。GetMapping(/products)publicListProductsearch(RequestParam(requiredfalse)Stringkeyword,RequestParam(requiredfalse)Stringcategory,RequestParam(defaultValueprice)StringsortBy){returnproductService.search(keyword,category,sortBy);}请求GET /products?keyword手机categoryelectronicssortByprice注意required false这个参数是可选的不传也不会报 400。如果你确定某个参数必须传比如分页的页码可以用required true默认值前端不传就直接返回 400 错误。多个值的情况一个参数还能接收多个值前端用同一个 key 传多次GetMapping(/users/batch)publicListUsergetByIds(RequestParamListLongids){returnuserService.getByIds(ids);}请求GET /users/batch?ids1,2,3或GET /users/batch?ids1ids2ids3Spring 会自动把多个值收集到List里。二、PathVariable从 URL 路径中取值PathVariable对应的是 URL 路径中用{xxx}占位的部分。GetMapping(/users/{id})publicUsergetUser(PathVariableLongid){returnuserService.getUserById(id);}前端请求GET /users/42{id}是一个路径变量PathVariable告诉 Spring去 URL 路径里把{id}位置的值取出来转成Long赋给id参数。适用场景标识具体资源的场景。RESTful 风格的 API 设计中用路径来表达操作的是哪个资源。GET /users/42 → 查看用户 42 PUT /users/42 → 更新用户 42 DELETE /users/42 → 删除用户 42 GET /orders/1001/items → 查看订单 1001 的商品列表路径中的42、1001就是资源标识用PathVariable接收。这种设计比GET /users?id42更符合 REST 语义URL 也更简洁直观。多个路径变量一个 URL 里可以有多个占位符GetMapping(/departments/{deptId}/employees/{empId})publicEmployeegetEmployee(PathVariableLongdeptId,PathVariableLongempId){returnemployeeService.get(deptId,empId);}请求GET /departments/5/employees/128两个占位符对应两个PathVariableSpring 会按名称匹配。三、RequestBody从请求体中取值RequestBody对应的是 HTTP 请求的 Body 部分。当前端发送 JSON 格式的数据时Spring 会用HttpMessageConverter默认是 Jackson把 JSON 反序列化成 Java 对象。PostMapping(/users)publicUsercreateUser(RequestBodyUserCreateRequestrequest){returnuserService.createUser(request);}// 请求体对应的 DTOpublicclassUserCreateRequest{privateStringname;privateStringemail;privateIntegerage;// getter/setter}前端请求POST /users Content-Type: application/json { name: 张三, email: zhangsanexample.com, age: 25 }Spring 看到RequestBody就知道把整个请求体读出来用 Jackson 解析成UserCreateRequest对象。字段名要和 JSON 的 key 对应类型要能转换否则直接报 400。适用场景提交复杂表单数据、创建或更新资源。当参数多、结构复杂、或者包含嵌套对象时RequestBody是最佳选择。// 嵌套对象的场景publicclassOrderCreateRequest{privateLonguserId;privateListOrderItemitems;privateAddressshippingAddress;privateStringpaymentMethod;}publicclassOrderItem{privateLongproductId;privateIntegerquantity;}publicclassAddress{privateStringprovince;privateStringcity;privateStringdetail;}这种嵌套结构用RequestParam一个个接几乎不可能用RequestBody一行搞定。三种注解对比对比维度RequestParamPathVariableRequestBody数据来源URL 查询参数?keyvalueURL 路径/users/{id}HTTP 请求体 BodyContent-Type无要求无要求通常application/json参数数量适合少量可选参数适合 1-2 个资源标识适合复杂/大量参数HTTP 方法任意任意通常 POST/PUT典型场景分页、筛选、排序资源标识、RESTful 路径创建、更新、复杂提交浏览器可见性地址栏可见地址栏可见不可见在请求体里一句话总结RequestParam管查询条件PathVariable管资源标识RequestBody管请求体。什么时候用哪个按 HTTP 语义来选与其记住注解的语法不如理解 HTTP 本身的语义。每个 HTTP 方法有它自己的含义参数怎么传跟着语义走就行了。GET 请求筛选和查询GET 用来获取资源。参数通常是可选的筛选条件放在 URL 查询参数里GetMapping(/articles)publicListArticlesearch(RequestParam(requiredfalse)Stringkeyword,RequestParam(defaultValue1)intpage,RequestParam(defaultValue20)intsize){returnarticleService.search(keyword,page,size);}如果要查某个具体资源用路径标识GetMapping(/articles/{id})publicArticlegetDetail(PathVariableLongid){returnarticleService.getById(id);}GET 请求没有请求体所以RequestBody在 GET 中没有意义。虽然 HTTP 规范没有明确禁止 GET 带 Body但大多数 HTTP 客户端和代理服务器会忽略 GET 的 BodySpring 默认也不支持。如果你发现自己想在 GET 里用RequestBody大概率是接口设计有问题应该改成 POST。POST 请求创建资源POST 用来提交数据、创建资源。数据通常比较复杂放在请求体里PostMapping(/articles)publicArticlecreate(RequestBodyValidArticleCreateRequestrequest){returnarticleService.create(request);}PUT 请求更新资源PUT 用来更新资源。被更新的资源用路径标识更新的内容放请求体PutMapping(/articles/{id})publicArticleupdate(PathVariableLongid,RequestBodyArticleUpdateRequestrequest){returnarticleService.update(id,request);}这里PathVariable和RequestBody同时出现——一个管更新谁一个管更新成什么职责非常清晰。DELETE 请求删除资源DELETE 用来删除资源被删除的对象用路径标识DeleteMapping(/articles/{id})publicvoiddelete(PathVariableLongid){articleService.delete(id);}把上面的规律总结成一张表HTTP 方法资源标识请求数据典型注解组合GETPathVariableRequestParam查询列表 / 查看详情POST—RequestBody创建资源PUTPathVariableRequestBody更新资源DELETEPathVariable—删除资源使用时注意事项RequestBody 不能和 RequestParam 混用在同一参数上有些新手会写出这种代码// 错误写法PostMapping(/users)publicUsercreate(RequestBodyRequestParamUserDTOdto){...}这两个注解的数据来源是矛盾的RequestBody从请求体取RequestParam从查询参数取。一个参数不可能同时从两个地方取值。Spring 会直接报错。正确的做法是分清楚哪些参数从查询参数来哪些从请求体来PostMapping(/users)publicUsercreate(RequestParam(defaultValuefalse)Booleannotify,// 查询参数RequestBodyUserDTOdto){// 请求体returnuserService.create(dto,notify);}PathVariable 的变量名要和路径占位符一致// 如果占位符叫 {userId}参数名也得叫 userIdGetMapping(/users/{userId})publicUsergetUser(PathVariableLonguserId){...}RequestBody 的校验RequestBody接收的对象通常需要校验。配合Valid注解和 JSR-303 校验注解一起用publicclassUserCreateRequest{NotBlank(message用户名不能为空)privateStringname;Email(message邮箱格式不正确)privateStringemail;Min(value0,message年龄不能为负数)privateIntegerage;}PostMapping(/users)publicUsercreate(RequestBodyValidUserCreateRequestrequest){returnuserService.create(request);}不加Valid校验注解不会生效前端传什么数据都能进来。接收数组或列表RequestBody可以直接接收 JSON 数组PostMapping(/users/batch)publicListUserbatchCreate(RequestBodyListUserCreateRequestrequests){returnuserService.batchCreate(requests);}前端传一个 JSON 数组就行[{name:张三,email:zhangsanexample.com},{name:李四,email:lisiexample.com}]小结三种注解的本质区别在于数据来源不同RequestParam从 URL 查询参数取PathVariable从 URL 路径取RequestBody从请求体取。不用死记语法只需要跟着 HTTP 语义走GET 查询用RequestParam资源标识用PathVariable复杂数据提交用RequestBody。