2

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:

  • spring-boot version 2.7.2
  • spring-boot-starter-webflux
  • spring-boot-starter-validation
Eugene
  • 5,269
  • 2
  • 14
  • 22
Pierre Recuay
  • 83
  • 1
  • 7

1 Answers1

1

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"
}
Eugene
  • 5,269
  • 2
  • 14
  • 22