Question

I have added the HttpPutFormContentFilter in web.xml.

Here is an action for reveiving both GET,POST,DELETE and PUT method.

@RequestMapping(value = "/**")
public ResponseEntity<byte[]> proxy(HttpServletRequest request,@RequestParam MultiValueMap<String, String> params, @RequestBody byte[] body, @RequestHeader MultiValueMap<String, String> headers) {

When I use POST and add the application/x-www-form-urlencoded header,I can get both the request body and request param.

When I use PUT and add the application/x-www-form-urlencoded header,I can get the request param,but I can't get request body!

There is any bug in the HttpPutFormContentFilter?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top