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 @ExceptionHandlers 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 )