Description
In Spring 5.3 it is possible that a MissingServletRequestParameterException
is thrown although a required parameter is provided (and therefore not missing). The same holds for a required path variable - even it is provided a MissingPathVariableException
may be thrown.
Moreover the MissingServletRequestParameterException
triggers a response status 400 in contrast to the MissingPathVariableException
which ends up in a response status 500.
The former happens due to the changes made in #23939: If a provided parameter (request parameter or path variable) is converted to null
it is handled as a missing value, see AbstractNamedValueMethodArgumentResolver.
I would like to propose:
- Adjust the response status.
- Throw a different exception (e.g.
IllegalArgumentException
) if a required parameter (request parameter or path variable) is provided but converted tonull
.
In a lengthy discussion with @rstoyanchev in #26088 (I thank his patience) I gave an example to explain that the current status is problematic:
- two users load the same book entity via its id, let's say 42
- one user deletes the book entity
- the other user continues using URLs with the id 42
I attached a small spring boot app with several test cases (github, spring_sandbox-master.zip).
With
@GetMapping(path = "/rp/book")
public Book getBookByRequestParam(@RequestParam("id") Book book) {
...
calling /rp/book?id=42
leads to a MissingServletRequestParameterException
which is also thrown if /edit
is called without a parameter or with an empty parameter - these cases can not be distinguished in an elegant way, see the tests
noRequestParamThrowsMissingServletRequestParameterException
emptyRequestParamThrowsMissingServletRequestParameterException
idOfDeletedBookThrowsMissingServletRequestParameterException
In contrast calling
@GetMapping({"/pv/book/{id}"})
public Book getBookByPathVariable(@PathVariable("id") Book book) {
...
with /pv/book/42
"only" leads to a misleading MissingPathVariableException
, but the method can not be called with no or an empty parameter (if /pv/book
is not mapped to a different method it is handled by the ResourceHttpRequestHandler
, see the test missingValueNotHandledByController
).
But a conflicting case can be constructed this way:
@GetMapping({"/cc/book", "/cc/book/{id}"})
public Book constructedCase(@PathVariable("id") Book book) {
...
Here calling /cc/book
and /cc/book/42
both lead to a MissingPathVariableException
- but as I said this is constructed resp. bad programming.