大数跨境
0
0

SpringBoot接口参数如何使用多个@RequestBody?

SpringBoot接口参数如何使用多个@RequestBody? Spring全家桶实战案例
2024-12-11
0
导读:SpringBoot接口参数如何使用多个@RequestBody?

Spring Boot 3实战案例合集》现已囊括超过60篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。

环境:SpringBoot3.2.5



1. 简介

在Controller接口方法参数上通过使用@RequestBody可以将请求体中的数据自动绑定到Java对象上。如下示例:

@RestController@RequestMapping("/requestbody")public class MultiRequestBodyController {
@PostMapping("") public Object save(@RequestBody User user) {    // TODO return "success" ; }}

@RequestBody注解在开发中几乎必用。上面示例是将请求体中的信息自动绑定到一个对象,如果请求的信息中包含2个对象的信息,是否可以像下面这样使用呢?

@PostMapping("")public Object save(@RequestBody User user,   @RequestBody Person person) {  // TODO  return "success" ;}

调用上面接口出现了错误

控制台也输出了如下信息

 Resolved [HttpMessageNotReadableException: Required request body is missing: public Object BodyController.save(com.pack.User,com.pack.Person)

提示缺少body。

@RequestBody注解的参数会通过RequestResponseBodyMethodProcessor进行解析,而这个处理过程就是通过调用ServletRequest#getInputStream方法获取输入流,从流中读取数据(获取到数据后再通过HttpMessageConverter进行转换,如:jackson)。处理第一个参数时从流中获取了到了数据然后绑定到了对象上,当处理第二个参数时,同样还是从当前的Request中获取输入流InputStream,而对于同一个Request对象,内部机制只允许调用一次。所以就出现了上面的错误。

2. 解决办法

2.1 将多个对象包装到DTO中

public class DTO {  private User user ;  private Person person ;  // getter, setter}

修改Controller接口

@PostMapping("")public Object save(@RequestBody DTO dto) {  // TODO  return "success" ;}

这样修改后,提交的请求参数就要修改

该种方法虽然解决了问题,也带来了一个问题,需要修改请求参数。但是这不是我们今天要将的重点,今天的重点是方法参数使用多个@RequestBody注解来接收不同的数据。

2.2 包装Request对象

要实现方法参数使用多个@RequestBody,就需要解决ServletRequest#getInputStream方法只能调用一次的问题。在Servlet规范中,我们可以通过自定义HttpServletRequestWrapper来定制Request相应的方法(该类实现了装饰器模式),如下示例:

public class PackHttpServletRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayInputStream bais ;
public PackHttpServletRequestWrapper(HttpServletRequest request) { super(request) ; ByteArrayOutputStream baos = new ByteArrayOutputStream() ; try { StreamUtils.copy(request.getInputStream(), baos) ; bais = new ByteArrayInputStream(baos.toByteArray()) ; } catch (IOException e) { e.printStackTrace() ; }  }
@Override public ServletInputStream getInputStream() throws IOException {    // 当再次调用该方法时,重置bais流;将缓冲区重置到标记0的位置 bais.reset() ; return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bais.read() ; } }; }
}

实现一个过滤器,该过滤器传递上面自定义的Request对象。

public class PackRequestWrapperFilter implements Filter {
@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)      throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req ; PackHttpServletRequestWrapper requestWrapper = new PackHttpServletRequestWrapper(request) ; chain.doFilter(requestWrapper, resp) ; }}

注册该过滤器,在SpringBoot环境下我们可以通过多种方式注册过滤器,下面是其中一种方式(@WebFilter注解更简单,不过要添加@ServletComponentScan注解)。

@BeanFilterRegistrationBean<Filter> packRequestWrapperFilter() {  FilterRegistrationBean<Filter> reg = new FilterRegistrationBean<>() ;  reg.setFilter(new PackRequestWrapperFilter()) ;  reg.setUrlPatterns(List.of("/*")) ;  return reg ;}

以上实现就可以在当前的请求中重复的调用getInputStream方法获取流。

注:ServletRequest#getReader方法只允许调用一次,所以有需要你也必须重写该方法

测试

控制台输出

注:一个方法上使用多个@RequestBody对性能肯定是有一定的影响,在上面已经提到了,读取到的数据会通过HttpMessageConverter进行转换为java对象,当你有多个@RequestBody时,那就要进行多次的读取到转换,所以应该考虑清楚再使用。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

SpringBoot3使用虚拟线程一定要小心了

SpringBoot一个非常强大的数据绑定类

@Qualifier高级技巧,你会几个?深入底层原理

性能提升!Spring Boot使用Guava中3大神技

必学!Spring Boot结合MDC全方位的日志跟踪(支持跨线程)

基于注解的Controller接口这些高级功能你都知道吗?

请一定牢记SpringBoot项目开发中的8个扩展接口

面试官:说说@Configuration与@Component有什么区别?

解锁Spring资源Resource的强大功能,提升开发效率

掌握Spring这些注入技巧让你事半功倍

【值得收藏】JDK10到21!新特性一网打尽!

必读!SpringBoot接口参数校验N种实用技巧大揭秘

想要精通SpringMVC?先从自定义核心组件开始,助你成为技术大佬

【纯干货】SpringCloud敏感信息配置揭秘,教你防止信息泄露!

Spring利器,数据格式化,让你的代码优雅有型!

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读195
粉丝0
内容832