Validation is crucial in Spring Boot applications. Here’s a complete guide to using @Valid and @Validated.

Basic Validation

Dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Bean Validation

public class CreateUserRequest {
    @NotBlank(message = "Name is required")
    private String name;
    
    @Email(message = "Invalid email format")
    @NotBlank
    private String email;
    
    @Min(value = 18, message = "Age must be at least 18")
    @Max(value = 120, message = "Age must be at most 120")
    private Integer age;
}

@Valid vs @Validated

@Valid

@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
    // Validates request object
}

@Validated

@RestController
@Validated
public class UserController {
    
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable @Min(1) Long id) {
        // Validates path variable
    }
}

Custom Validators

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface PhoneNumber {
    String message() default "Invalid phone number";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.matches("^\\+?[1-9]\\d{1,14}$");
    }
}

Validation Groups

public interface CreateGroup {}
public interface UpdateGroup {}

public class UserRequest {
    @NotNull(groups = UpdateGroup.class)
    private Long id;
    
    @NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
    private String name;
}

@PostMapping("/users")
public ResponseEntity<User> create(@Validated(CreateGroup.class) @RequestBody UserRequest request) {
    // Only validates CreateGroup fields
}

Error Handling

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error ->
            errors.put(error.getField(), error.getDefaultMessage())
        );
        return ResponseEntity.badRequest()
            .body(new ErrorResponse("Validation failed", errors));
    }
}

Best Practices

  1. Validate at controller level
  2. Use appropriate annotations
  3. Create custom validators
  4. Handle validation errors
  5. Use validation groups

Conclusion

Spring Boot validation provides:

  • Declarative validation
  • Custom validators
  • Error handling
  • Type safety

Validate your data properly! ✅