patternjavaspringModerate
@Valid triggers MethodArgumentNotValidException but error details are not returned to client
Viewed 0 times
@ValidvalidationMethodArgumentNotValidExceptionBindingResultfield errorsBean Validation
Error Messages
Problem
Adding @Valid to a @RequestBody parameter causes Spring to validate the input and throw MethodArgumentNotValidException on failure, but the default error response is a generic 400 with no field-level detail, leaving clients unable to show meaningful validation errors.
Solution
Handle MethodArgumentNotValidException in a @ControllerAdvice to extract and return field errors:
Use Bean Validation annotations on the DTO:
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidation(
MethodArgumentNotValidException ex) {
Map<String, String> fieldErrors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
fe -> Objects.requireNonNullElse(fe.getDefaultMessage(), "invalid"),
(a, b) -> a + "; " + b
));
return ResponseEntity.badRequest().body(Map.of(
"status", 400,
"errors", fieldErrors
));
}
}Use Bean Validation annotations on the DTO:
public record CreateOrderRequest(
@NotBlank String customerId,
@NotEmpty List<@Valid OrderItemRequest> items,
@DecimalMin("0.01") BigDecimal total
) {}Why
Spring's default error handler for MethodArgumentNotValidException returns minimal information. A custom @ExceptionHandler replaces this handling and lets you control the response structure.
Gotchas
- @Valid must be on the @RequestBody parameter, not just on the DTO class itself
- Nested objects require @Valid on the field inside the DTO to trigger cascade validation
- @Validated (Spring) vs @Valid (Jakarta) — @Validated supports validation groups, @Valid does not
Revisions (0)
No revisions yet.