Master Spring Boot Validation: 10 Essential Tips & Custom Annotations
This article explains ten practical techniques for implementing robust parameter validation in Spring Boot applications, covering built‑in annotations, custom constraints, server‑side checks, internationalization, validation groups, cross‑field rules, exception handling, testing, and the importance of complementary client‑side validation.
Introduction
Parameter validation is crucial for stability and security in Spring Boot applications. This article presents ten practical techniques for effective validation.
1. Use Validation Annotations
Spring Boot provides built‑in validation annotations such as
@NotNull,
@NotEmpty,
@NotBlank,
@Min,
@Max,
@Pattern, and
@Emailto enforce constraints on fields.
@NotNull: field must not be null.
@NotEmpty: collection must not be empty.
@NotBlank: string must contain non‑whitespace characters.
@Min/
@Max: numeric range.
@Pattern: regex pattern.
@Email: valid email address.
Example:
<code>public class User {
@NotNull
private Long id;
@NotBlank
@Size(min = 2, max = 50)
private String firstName;
@NotBlank
@Size(min = 2, max = 50)
private String lastName;
@Email
private String email;
@NotNull
@Min(18)
@Max(99)
private Integer age;
@NotEmpty
private List<String> hobbies;
@Pattern(regexp = "[A-Z]{2}\\d{4}")
private String employeeId;
}</code>2. Create Custom Validation Annotations
When built‑in annotations are insufficient, define a custom constraint annotation and validator, e.g.,
@UniqueTitleto ensure a post title is unique.
<code>@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueTitleValidator.class)
public @interface UniqueTitle {
String message() default "Title must be unique";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}</code>Implement the validator:
<code>@Component
public class UniqueTitleValidator implements ConstraintValidator<UniqueTitle, String> {
@Autowired
private PostRepository postRepository;
@Override
public boolean isValid(String title, ConstraintValidatorContext context) {
if (title == null) {
return true;
}
return Objects.isNull(postRepository.findByTitle(title));
}
}</code>Apply to the entity:
<code>public class Post {
@UniqueTitle
private String title;
@NotNull
private String body;
}</code>3. Server‑Side Validation
Use DTOs with validation annotations and enable method‑level validation with
@Validatedand
@Validin controllers.
<code>public class UserDTO {
@NotBlank
private String username;
@NotBlank
private String password;
}</code> <code>@RestController
@RequestMapping("/users")
@Validated
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDto) {
userService.createUser(userDto);
return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
}
}</code>4. Provide Meaningful Error Messages
Customize messages via the
messageattribute or externalized i18n files.
5. Internationalize Error Messages
Define
messages.propertiesand locale‑specific files, then configure
MessageSourceand
LocalValidatorFactoryBeanto use them.
6. Group Validation
Define validation groups (e.g.,
EmailNotEmptyand
Default) to apply different rules based on context.
<code>public class User {
@NotBlank(groups = Default.class)
private String firstName;
@NotBlank(groups = Default.class)
private String lastName;
@Email(groups = EmailNotEmpty.class)
private String email;
public interface EmailNotEmpty {}
public interface Default {}
}</code>Use
@Validated({EmailNotEmpty.class})or
@Validated({Default.class})on controller parameters.
7. Cross‑Field Validation
Create a class‑level annotation such as
@EndDateAfterStartDatewith a validator that checks
endDate.isAfter(startDate).
<code>@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EndDateAfterStartDateValidator.class)
public @interface EndDateAfterStartDate {
String message() default "End date must be after start date";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}</code> <code>public class TaskForm {
@NotNull @DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
@NotNull @DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
}</code>8. Exception Handling for Validation Errors
Use
@RestControllerAdvicewith an
@ExceptionHandler(MethodArgumentNotValidException.class)to return a structured error response.
<code>@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", status.value());
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());
body.put("errors", errors);
return new ResponseEntity<>(body, headers, status);
}
}</code>9. Test Validation Logic
Write unit tests with
Validatorto assert expected constraint violations.
<code>@DataJpaTest
public class UserValidationTest {
@Autowired private TestEntityManager entityManager;
@Autowired private Validator validator;
@Test
public void testValidation() {
User user = new User();
user.setFirstName("John");
user.setLastName("Doe");
user.setEmail("invalid email");
Set<ConstraintViolation<User>> violations = validator.validate(user);
assertEquals(1, violations.size());
assertEquals("must be a well‑formed email address", violations.iterator().next().getMessage());
}
}</code>10. Consider Client‑Side Validation
Client‑side checks improve user experience but must be complemented by server‑side validation for security.
Conclusion
Effective validation is essential for stable and secure web applications. Spring Boot offers a comprehensive set of tools to simplify validation, and following the practices above helps maintain robust, user‑friendly services.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.