Adding another answer since I ran into the same issue, but solved it in a different manner.
I too had a request logging filter, which wrapped incoming requests and cached the response input stream in a similar manner as described in the answer by Jonas Pedersen. Spring Boot was updated from 2.1.2.RELEASE to 2.3.4.RELEASE
I wrap the incoming request in a caching request wrapper which caches the input stream.
For me, the issue was that for some (as of yet unknown) reason the request.getParameterValue(String key) method return null, even though the wrapped request clearly had a non-empty parameter map.
Simply accessing the wrapped requests parameter map solved the issue for me...very strange.
The original wrapper class, working with Spring Boot 2.1.2.RELEASE:
public class CachingRequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public CachingRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new CachedInputStream(this.cachedBody);
}
@Override
public BufferedReader getReader() throws IOException {
ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(this.cachedBody);
String encoding = StringUtils.isEmpty(this.getCharacterEncoding())
? StandardCharsets.UTF_8.name()
: this.getCharacterEncoding();
return new BufferedReader(new InputStreamReader(byteArrayInputStream, encoding));
}
}
Implementation of the CachedInputStream class is left out for brevity.
Simply accessing the wrapped request map seemed to solve the whole issue. For Spring Boot 2.3.4.RELEASE this version works (I removed some details):
public class CachingRequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedBody;
private final Map<String, String[]> parameterMap;
public CachingRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
parameterMap = request.getParameterMap(); // <-- This was the crucial part
InputStream requestInputStream = request.getInputStream();
this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
}
@Override
public Map<String, String[]> getParameterMap() {
return this.parameterMap; // this was added just to satisfy spotbugs
}
}
I have not bothered to dig deeper into the issue, so I can not say what changed between the two Spring Boot versions or what accessing the parameter map in the wrappers constructor solved it.