javaspring-bootspring-webfluxspring-restjavax.validation

How to group request javax validations?


I have test spring-boot-starter-validation behaviour and noticed that: request body is validated first throwing a WebExchangeBindException and then request path&query parameters are validated throwing a ConstraintViolationException. So, how to join these two groups of constraints in a single Exception catched in a single response body?

Expected response body:

{
  "address": "must not be blank",
  "mail": "must be a well-formed email address",
  "floor": "floor cannot be null",
  "control.mark": "must be less than or equal to 5",
  "control.infect": "must be greater than 0",
  "control.age": "must be greater than or equal to 5"
}

Actual request body fields constraints:

{
  "address": "must not be blank",
  "mail": "must be a well-formed email address",
  "floor": "floor cannot be null"
}

Actual query and path parameters constraints:

{
  "control.mark": "must be less than or equal to 5",
  "control.infect": "must be greater than 0",
  "control.age": "must be greater than or equal to 5"
}

Here is an integration test for a better understanding link

Dependencies:


Solution

  • You need to use one unified ConstraintViolationException exception and gather all constraints into one group.
    It is possible via @Validated(Group.class) annotation. Validation group documentation.

    1. Create an interface for your validation group

    public interface Group {
    }
    

    2.Apply group for your RestController

    @RestController
    @Validated
    @Slf4j
    public class BookController {
    
        @PostMapping(value = "/control/{age}/mark/{mark}")
        @Validated(Group.class)
        public Object control(
                @PathVariable("age")
                @Min(value = 5, groups = Group.class) Integer age,
    
                @PathVariable("mark")
                @Max(value = 5, groups = Group.class) Integer mark,
    
                @Min(value = 3, groups = Group.class) @RequestParam(name = "conclusion") Integer conclusion,
    
                @Positive(groups = Group.class) @RequestParam(name = "infect") Integer infect,
    
                @Valid
                @RequestBody Book book
        ) {
    
          return new Book();
        }
    }
    

    3. Apply group for your transfer object

    @Data
    public class Book {
    
        @NotBlank(groups = Group.class)
        private String address;
    
        @NotNull(message = "floor cannot be null", groups = Group.class)
        private String floor;
    
        @Email(groups = Group.class)
        private String mail;
    }
    

    Output:

    {
      "control.mark": "must be less than or equal to 5",
      "control.book.mail": "must be a well-formed email address",
      "control.infect": "must be greater than 0",
      "control.book.floor": "floor cannot be null",
      "control.book.address": "must not be blank",
      "control.age": "must be greater than or equal to 5"
    }