How to Use Multiple @RequestBody Parameters in Spring Boot 3

This article explains why a Spring Boot controller can read the request body only once, demonstrates wrapping multiple objects in a DTO, and provides a custom HttpServletRequestWrapper with a filter to enable multiple @RequestBody parameters while noting the performance impact.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Use Multiple @RequestBody Parameters in Spring Boot 3

1. Introduction

Using @RequestBody on a controller method binds the request body to a Java object. Only one request body can be read because the underlying ServletRequest#getInputStream can be called only once.

2. Solution

2.1 Wrap multiple objects in a DTO

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

Controller:

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

2.2 Wrap the HttpServletRequest

Define a custom HttpServletRequestWrapper that caches the input stream so it can be read multiple times.

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.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(); }
    };
  }
}

Filter that replaces the request with the wrapper:

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);
  }
}

Register the filter in Spring Boot:

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

The above configuration allows multiple @RequestBody parameters by reading the cached stream each time.

Note: Using multiple @RequestBody parameters incurs performance overhead because the request body is read and converted multiple times.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring Bootfilter@RequestBodyHttpServletRequestWrapper
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.