According to the Servlet specification (see chapters 1.1 and 3.1.1), when you receive a POST request and the content type is application/x-www-form-urlencoded
, that form data needs to be made available through the HttpServletRequest#getParameterXXX()
methods. This is not true for PUT requests.
In all cases, the body of the HttpServletRequest
is available as an InputStream
to Servlet
and Filter
instances.
For POST, when Spring sees
@RequestParam MultiValueMap<String, String> params
it uses a RequestParamMapMethodArgumentResolver
to resolve the argument. This reads directly from the HttpServletRequest
parameter map. When Spring sees
@RequestBody byte[] body
it uses a RequestResponseBodyMethodProcessor
which reads from the HttpServletRequest
InputStream
using a ByteArrayHttpMessageConverter
to fill up a byte[]
.
Once you've read the HttpServletRequest
InputStream
, you cannot re-read (in the default configuration).
For PUT, because the Servlet container doesn't store form parameters in the HttpServletRequest
for PUT requests, Spring decided to introduce the HttpPutFormContentFilter
. This Filter
reads the HttpServletRequest
body to populate its own parameter map in an HttpServletRequestWrapper
which it hands you.
Once this is done, the request parameters are available to the RequestParamMapMethodArgumentResolver
, but when the RequestResponseBodyMethodProcessor
tries to fill up the byte[]
, there are no bytes left in the InputStream
so it leaves it empty.
One workaround is to create your own Filter
(which must execute before the HttpPutFormContentFilter
, so it's kind of bad practice), which wraps the HttpServletRequest
in a HttpServletRequestWrapper
which buffers the InputStream
in a ByteArrayInputStream
so you can re-read it as many times as necessary.