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.
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
<code>public class DTO {
private User user;
private Person person;
// getter, setter
}</code>Controller:
<code>@PostMapping("")
public Object save(@RequestBody DTO dto) {
// TODO
return "success";
}</code>2.2 Wrap the HttpServletRequest
Define a custom HttpServletRequestWrapper that caches the input stream so it can be read multiple times.
<code>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(); }
};
}
}</code>Filter that replaces the request with the wrapper:
<code>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);
}
}</code>Register the filter in Spring Boot:
<code>@Bean
FilterRegistrationBean<Filter> packRequestWrapperFilter() {
FilterRegistrationBean<Filter> reg = new FilterRegistrationBean<>();
reg.setFilter(new PackRequestWrapperFilter());
reg.setUrlPatterns(List.of("/*"));
return reg;
}</code>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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.