A de facto rule of thumb for the Servlet paradigm is that a ServletRequest's body content can only be read once. This implies at least a couple limitations:
The content retrieval methods (getReaderandgetInputStream) cannot be used in combination. (See #21042.)
Each content retrieval method (getReaderorgetInputStream) will read the body only once.
This latter limitation is the subject of this ticket. Although unlike #1, this behavior is not explicitly specified in the interface documentation, majorServletimplementations appear to concur in returning the same object reference for successive calls to either getReader or getInputStream. This means that it's effectively a bug for application code to attempt to read the body contents twice from two such separate calls.
However, the current implementation of MockHttpServletRequest constructs a fresh object each time one of these methods is called. Revising this implementation to retain the returned reader or stream reference for successive calls will better reflect real-world implementations and enhance the framework's value by allowing developers to catch one more class of bugs prior to deploying to an actual container.
Affects: 4.3.14, 5.0.3
#21042 MockHttpServletRequest shouldn't allow calls to both getReader and getInputStream
#21906 MockHttpServletRequest doesn't reset InputStream/Reader on setContent
Addressed in a revised fashion along with #21042, also covering our internal copy of MockHttpServletRequest and refactoring several of our tests which tried to reuse a request instance for several tests.
As currently implemented, this change breaks Spring REST Docs. It will, I believe, also break any other tests that rely on reading the body of a request obtained from MvcResult.getRequest().
To document a request body, REST Docs relies on being able to read the body of the request obtained from the MvcResult. The change made for this issue prevents that as, typically, the body has already been read as part of the request being handled.
I wonder if the improvement being made for this issue could be retained while also allowing the body of a request that's obtained from MvcResult.getRequest() to be read multiple times. This would better reflect real-world implementations for request handling code while also allowing tests to read the body as many times as necessary to make their assertions.
The InputStream or Reader are just wrappers around the actual byte content that mock request was configured with. To access that you can use still getContentAsByteArray() and getContentAsString() as many times as necessary. That is what the MockMvc result matchers do. Does Spring REST Docs need to use getInputStream() for some reason?
I suspect that REST Docs is using getInputStream() simply because getContentAsByteArray() didn't exist when the code was written. I see no reason why REST Docs can't move to it. All it's doing at the moment is reading the input stream into a byte array anyway. Thanks for the pointer.
This is a massive breaking change IMO. Previous versions of MockHttpServletRequest allows setting the content of the request to a different value and calling getReader() or getInputStream again to obtain the new content.
IMO if this change is to be retained then the reader should at least be reset when setContent is called.