Issue
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
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"
}
Answered By - Eugene
Answer Checked By - Katrina (JavaFixing Volunteer)