I have a Spring @RestController
that has a POST endpoint defined like this:
@RestController
@Validated
@RequestMapping("/example")
public class Controller {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<?> create(@Valid @RequestBody Request request,
BindingResult _unused, // DO NOT DELETE
UriComponentsBuilder uriBuilder) {
// ...
}
}
It also has an exception handler for javax.validation.ConstraintViolationException
:
@ExceptionHandler({ConstraintViolationException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
ProblemDetails handleValidationError(ConstraintViolationException e) {...}
Our Spring-Boot app is using spring-boot-starter-validation
for validation. The Request
object uses javax.validation.*
annotations to apply constraints to the various fields like this:
public class Request {
private Long id;
@Size(max = 64, message = "name length cannot exceed 64 characters")
private String name;
// ...
}
As presented above, if you POST a request with an invalid Request, the validation will throw a ConstraintViolationException, which will be handled by the exception handler. This works, we have unit tests for it, all is good.
I noticed that the BindingResult
in the post method wasn't used (the name _unused
and comment //DO NOT DELETE
were sort of red flags.) I went ahead and deleted the parameter. All of a sudden, my tests broke -- the inbound request was still validated, but it would no longer throw a ConstraintValidationException ... now it throws a MethodArgumentNotValidException
! Unfortunately I can't used this other exception because it doesn't contain the failed validation in the format that I need (and doesn't contain all the data I need either).
Why does the BindingResult
presence in the argument list control which exception is thrown? How can I removed the unused variable and still throw the ConstraintViolationException
when the javax.validation determines that the request body is invalid?
Spring-Boot 2.5.5
- spring-boot-starter-web
- spring-boot-starter-validation
OpenJDK 17.
@InitBinder
in the controller directly if I want to only disable the data binder in that one controller, or must I use a controller advice and apply it globally? – Gayton