Closed membersound closed 1 week ago
The change of behavior seems to have been introduced in Spring Framework 6.1:
params
contains both the ones defines in query parameters and request bodyIn AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters
:
Spring Boot 3.1.12
((ServletServerHttpRequest)inputMessage).getBody()
is a ByteArrayInputStream
((ServletServerHttpRequest)inputMessage).getBody().readAllBytes()
returns 22 bytes as expected.
Spring Boot 3.2.8
((ServletServerHttpRequest)inputMessage).getBody()
returns a CoyoteInputStream
((ServletServerHttpRequest)inputMessage).getBody().readAllBytes()
returns 0 bytes
Both uses Tomcat 10.1.x.
Likely a regression caused by gh-31327 (via 8fa428f825323d6bc0d3051ad44d84a249fcaf39) as you have already found @membersound (could have been nice to mention it in the issue description, please share all your findings next time please).
@rstoyanchev As I lack a bit of context on those changes, could you please provide guidance on how to handle this when you are back?
In the Servlet API, request parameter access causes the request body to be parsed, and it can't be read again, e.g. via @RequestBody
. In ServletServerHttpRequest#getBody
we try to make up for that by re-constructing the body from the request parameter map, but this is fragile.
In 6.0.x we accepted an optimization in StringHttpMessageConverter
to read the body via readNBytes
if there is a content-length, but the content-length can get out of sync if the reconstructed body ends up picking request parameters from the query. This is why the decision in #31327 was to back out of the feature if there is both a request body and a query string as we can't properly implement it in that case. We could try to exclude request parameters from the query when reconstructing the body, but that will likely cause regressions elsewhere, and could also prove fragile, e.g. if the same parameter name is present in both the body and the query.
I think the best thing is to not rely on the feature, which can only be properly implemented at the level of the Servlet container. In other words don't expect to be able to read form data via @RequestBody
reliably. Instead, for form data specifically, stick to using request parameters. For example for the above:
@PostMapping(value = "/example", consumes = APPLICATION_FORM_URLENCODED_VALUE)
public Mono<String> namedsqlPostForm(@RequestParam MultiValueMap<String, String> params) {
// ...
}
Based on @rstoyanchev feedback, I am turning this regression issue to a documentation one.
@membersound Please follow Rossen's guidance for your use case.
I upgraded from spring-boot v2 -> v3. My tests could not detect the following issue, even though I tested the scenario. The following problem does only occur in live, but not in tests:
In the following example, the
@RequestBody
parameters are missing in case when using additionalquery parameters
.The following works (body params are filled in controller):
The following fails (body params are missing in controller):
Result LIVE:
I attached a sample, which fails in live mode, but succeeds in junit test mode. Switching the example back to spring-boot 2.x, also the live mode works.
Test:
spring-web-example.zip