Every request from the outside world is untrusted until proven otherwise. Bean Validation lets you attach constraints to fields declaratively, and Spring runs them before your handler ever sees the data.
Annotate the request
Constraints live on the DTO. Combine them freely — a field can be both non-blank and size-limited.
public record CreateUserRequest(
@NotBlank @Size(max = 80) String name,
@Email String email,
@Min(18) int age
) {}Trigger validation with @Valid
Adding @Valid to the parameter makes Spring validate the body and throw
MethodArgumentNotValidException on failure.
@PostMapping("/api/users")
@ResponseStatus(HttpStatus.CREATED)
public UserResponse create(@Valid @RequestBody CreateUserRequest request) {
return service.create(request);
}Turn violations into clean responses
Catch the validation exception in a @RestControllerAdvice and map field errors
to a structured response the client can act on.
@RestControllerAdvice
public class ValidationHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handle(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(e ->
errors.put(e.getField(), e.getDefaultMessage()));
return errors;
}
}Takeaways
- Declare constraints on DTOs, not scattered through service code.
@Validtriggers validation automatically before the handler runs.- Map violations to a consistent 400 response with field-level detail.
For rules that span multiple fields, write a custom class-level constraint rather than leaking the logic into your service.