Spring Boot Validation
Validation checks that the data sent by a client meets your rules before your app processes it. Reject bad data at the API boundary — do not let it reach your service layer or database.
Adding the Validation Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Annotating a Request DTO
Place constraint annotations on the fields of your request class:
public class CreateUserRequest {
@NotBlank(message = "Name is required")
@Size(min = 2, max = 50, message = "Name must be 2–50 characters")
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Must be a valid email address")
private String email;
@NotNull(message = "Age is required")
@Min(value = 18, message = "Must be at least 18")
@Max(value = 120, message = "Must be 120 or below")
private Integer age;
@NotNull
@Pattern(regexp = "^[+]?[0-9]{10,13}$", message = "Invalid phone number")
private String phone;
// Getters and setters
}
Triggering Validation in the Controller
Add @Valid to the @RequestBody parameter. Spring runs validation before the method body executes:
@PostMapping("/users")
public ResponseEntity<UserResponse> createUser(
@Valid @RequestBody CreateUserRequest request ← @Valid triggers validation
) {
UserResponse saved = userService.create(request);
return ResponseEntity.status(201).body(saved);
}
If validation fails, Spring throws MethodArgumentNotValidException before your method runs.
Handling Validation Errors
Add this to your global exception handler (covered in the Exception Handling topic):
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex
) {
Map<String, String> errors = new LinkedHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(err ->
errors.put(err.getField(), err.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
Client receives a clear error response:
HTTP 400 Bad Request
{
"name": "Name is required",
"email": "Must be a valid email address",
"age": "Must be at least 18"
}
Common Validation Annotations
Annotation Validates ────────────────────────── ────────────────────────────────────────── @NotNull Value is not null @NotBlank String is not null, empty, or whitespace @NotEmpty String or collection is not null or empty @Size(min, max) String/collection length is within range @Min(value) Number is >= value @Max(value) Number is <= value @Email Valid email format @Pattern(regexp) Matches a regular expression @Positive Number is greater than 0 @PositiveOrZero Number is >= 0 @Future Date is in the future @Past Date is in the past @DecimalMin / @DecimalMax Decimal number constraints
Validation Flow Diagram
Client sends POST /users with body
│
▼ @Valid
Spring validates request fields
│
├── All valid? ──▶ Method runs normally → 201 Created
│
└── Invalid? ──▶ MethodArgumentNotValidException
│
▼
GlobalExceptionHandler
│
▼
400 Bad Request with field errors
Validating Path Variables and Query Params
Add @Validated at the class level to validate non-body parameters:
@RestController
@Validated ← Required for method-level validation
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(
@PathVariable @Positive(message = "ID must be positive") Long id
) {
return userService.findById(id);
}
}
Custom Validation Annotation
Create a reusable custom constraint when built-in annotations are not enough:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
public @interface UniqueEmail {
String message() default "Email already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class UniqueEmailValidator
implements ConstraintValidator<UniqueEmail, String> {
@Autowired
private UserRepository userRepo;
@Override
public boolean isValid(String email, ConstraintValidatorContext ctx) {
return !userRepo.existsByEmail(email);
}
}
Summary
- Add
spring-boot-starter-validationto enable Bean Validation - Annotate DTO fields with
@NotBlank,@Email,@Min, and other constraints - Add
@Validto the@RequestBodyparameter to trigger validation - Handle
MethodArgumentNotValidExceptionin your global exception handler - Create custom annotations for business-rule validations like unique email checks
