Hi,
when upgrading to Spring-Boot 3.2.2 and therefore SF 6.1.3 we noticed a change in behaviour that causes one of our @ExceptionHandler
s not to be triggered anymore, when the @RequestBody
fails validation (in this case when Body#target
is null).
@PostMapping(value = "hello", produces = MediaType.TEXT_PLAIN_VALUE)
public String hello(@RequestBody @Valid @NotNull Body body) {
return "Hello " + body.target;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
System.out.println(ex.getMessage());
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
public static class Body {
@NotNull
@Valid
private String target;
public Body() {}
public void setTarget(String target) {
this.target = target;
}
}
Interestingly, this can be worked around by removing the @NotNull
from the method parameter.
I think this is somewhat related to https://github.com/spring-projects/spring-framework/issues/31711 but without the indication that the fix causes the additional annotations not to work anymore or respectively breaking the validation somehow if present.
I've created a minimal reproducer under https://github.com/dreis2211/method-argument-validation-bug to ease testing. Rolling back to SB 3.2.1 fixes it, similar to removing the @NotNull
annotation. I'd appreciate if this is either fixed or explicitly documented. Use cases with List
request bodies in combination with a @NotEmpty
seem to be unaffected because they don't seem to throw a MethodArgumentNotValidException
in the first place (also with SB 3.2.1).
In case the @NotNull
annotation is not supported under certain circumstances, feel free to turn this into a documentation issue.
Let me know if you need anything. Cheers, Christoph
Comment From: quaff
@NotNull
@Valid
private String target;
You should remove @Valid
here.
Comment From: dreis2211
@quaff without @Valid
no validation happens at all.
Comment From: ChrAh88
I think instead of MethodArgumentNotValidException the HandlerMethodValidationException is thrown.
Is your Controller annotated with @Validated
? If not, can you try it?
Comment From: dreis2211
@ChrAh88 Class-level @Validated
+ method level @Valid
would also work. It's not so much about strategies on how to workaround the issue. In my case I can also remove the @NotNull
and it works likewise - as stated above.
Method validation on SF 6.1 should not need the @Validated
anymore to apply method level validation, anyway.
As said. I'm also totally fine with having this as a documentation-only issue, but it clearly needs clarification what is supported and what is not.
Comment From: crazyav1
Same issue after update to 6.1.3 instead of MethodArgumentNotValidException the HandlerMethodValidationException is thrown.
Comment From: quaff
@quaff without
@Valid
no validation happens at all.
According to the Javadoc, @Valid
should be used for nested property, obviously String
is not.
Have you tried to remove @NotNull
from method parameter and @Valid
from field?
public String hello(@RequestBody @Valid /*@NotNull*/ Body body) {
return "Hello " + body.target;
}
public static class Body {
@NotNull
/*@Valid*/
private String target;
public Body() {}
public void setTarget(String target) {
this.target = target;
}
}
Comment From: quaff
Same issue after update to 6.1.3 instead of MethodArgumentNotValidException the HandlerMethodValidationException is thrown.
I think it's expected if you remove @Validated
from class level then built-in web method validation will be used, see https://github.com/spring-projects/spring-framework/issues/30645.
@dreis2211 Can you confirm that HandlerMethodValidationException
is triggered instead of MethodArgumentNotValidException
with your case?
Comment From: crazyav1
@Validated
requirement seems like breaking change to me, but I don't find this mentioned anywhere . And if you try to find any tutorial on @valid
everyone is excpecting MethodArgumentNotValidException.
Comment From: dreis2211
@quaff HandlerMethodValidationException
is thrown. With a slightly adjusted error handler this would also work.
I don't want to repeat myself, but if this is a documentation-only issue I'm totally fine with that. But I think it needs documentation what the preferred way is and what is not supported.
Comment From: ChrAh88
@dreis2211 it's documented here: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html Second, if Java Bean Validation is present AND other method parameters, e.g. @RequestHeader, @RequestParam, @PathVariable have @Constraint annotations, then method validation is applied to all method arguments, raising HandlerMethodValidationException in case of validation errors.
With the fix of #31711, also SmartValidators with contained jakarta validator are respected. In case of spring boot the validator "ValidatorAdapter" is a SmartValidator with a contained jakarta validator.
But yes, spring boots upgrade instructions to version 3.2 or the release notes for 3.2.2 should be updated in my opinion. https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#upgrading-from-spring-boot-31
Comment From: crazyav1
@ChrAh88 From documentation it seems that if only @Valid @RequestBody SomeBody body
is used in method parameters, the only MethodArgumentNotValidException
is raised, and then we add some other annotation to other parameter,e.g. @Valid @RequestBody SomeBody body, @NotNull @RequestParam Param param
will raise HandlerMethodValidationException
even if validation error still in body?
Comment From: rstoyanchev
@ChrAh88 thanks for the analysis, which is spot on. And @crazyav1 you're right that the documentation needs a little update since method validation applies if any method parameter has constraint annotations.
Comment From: dreis2211
@rstoyanchev What is your recommendation for the use-case at hand? Dropping the @NotNull
, changing the @ExceptionHandler
(aka creating a duplicated one for HandlerMethodValidationException
) or using @Validated
or a combination of things?
Comment From: rstoyanchev
@dreis2211, I would suggest dropping the @NotNull
since @RequestBody
has a required
flag, but generally both exceptions could arise from controller methods and should be handled in some global @ExceptionHandler
method like an @ControllerAdvice
extension of ResponseEntityExceptionHandler
.
Comment From: dreis2211
Thanks. Exactly what I was hoping for (because I did it already :D )