I'm working on a Spring Boot (3.4.2) application where I'm using ContentCachingRequestWrapper to allow multiple reads of the request body. However, I'm encountering an issue where after the request body has been read once (e.g., by a filter or interceptor), all subsequent reads return an empty stream (read() == -1).
What’s Happening?
- A filter or interceptor wraps the request in ContentCachingRequestWrapper.
- An earlier component (in my case, a pre-handler that is parsing the request to determine it's type) reads the body once using request.getReader(), which makes nested calls down to a custom CachedBodyServletRequest class (based on https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times)
- Later on in the filter chain, when Spring is attempting to do readWithMessageConverters, it creates an EmptyBodyCheckingHttpInputMessage, which in turn calls request.getBody, that calls getInputStream() on the CachedBodyServletRequest object. This returns -1 which causes the subsequent logic to fail as body is null and a subsequent HttpMessageNotReadable exception.
- Debugging shows that inside ContentCachingRequestWrapper#getInputStream(), there's a conditional check:
public ServletInputStream getInputStream() throws IOException { if (this.inputStream == null) { this.inputStream = new ContentCachingInputStream(getRequest().getInputStream()); } return this.inputStream;}
Since inputStream is not null after the first read in the earlier pre-handler, Spring returns the same (now exhausted) stream instead of creating a new one.However, getContentAsByteArray() still contains the full request body.
Since ContentCachingRequestWrapper caches the request body, I expected getInputStream() to return a fresh InputStream that can be read multiple times—similar to getContentAsByteArray().
Observed Behavior
If a component reads the body early, subsequent calls to getInputStream() return an empty stream (read() == -1).Spring does not seem to refresh the InputStream even though the body is cached.
Is this the expected behavior of ContentCachingRequestWrapper?If so, what's the best way to reset or refresh the input stream for later reads?
Is this a bug with Spring or am I fundamentally misunderstanding something about request body caching and filter chains?