Best Practices for Validation in Spring 6.0+ and Spring Boot 3.0+
Written on
Understanding Validation in Spring
If you're still relying on if/else statements to perform validation in Spring 6.0 or Spring Boot 3.0, it's time to upgrade your approach. Implementing best practices can significantly enhance the reliability of your application.
For web services, it is crucial to conduct parameter validation in the Controller layer to prevent illegal parameters from affecting business logic. Typically, request parameters fall into two categories:
- POST and PUT Requests: Utilize @RequestBody to pass parameters.
- GET Requests: Use @RequestParam or @PathVariable for parameter passing.
Prerequisites:
- Spring 6.0+
- Spring Boot 3.0+
The Java API Specification (JSR303) lays out the standard validation API for Java Beans, although it does not provide a concrete implementation. Hibernate Validator serves as a robust implementation of this standard, offering validation annotations such as @Email and @Length. Spring Validation acts as a wrapper around Hibernate Validator, facilitating automatic validation of Spring MVC parameters.
Next, let’s delve into how to effectively use Spring Validation within a Spring Boot project.
Spring Boot's Validation Library
In Spring Boot 3.0+, the validation library has transitioned to jakarta.validation. You can incorporate it by adding the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
POST and PUT Requests
For POST and PUT requests, leverage @RequestBody to pass parameters. The backend should receive a Data Transfer Object (DTO). By annotating this DTO with @Validated, you enable automatic parameter validation. For instance, consider a User interface that mandates specific length constraints for fields:
@Data
public class UserDTO {
private Long userId;
@NotNull
@Length(min = 2, max = 10)
private String userName;
@NotNull
@Length(min = 6, max = 20)
private String account;
@NotNull
@Length(min = 6, max = 20)
private String password;
}
#### Example Controller Method
@PostMapping("/save")
public Result saveUser(@RequestBody @Validated UserDTO userDTO) {
// ...
return Result.ok();
}
GET Requests
For GET requests, use @RequestParam or @PathVariable. If many parameters are involved (more than six, for example), it’s advisable to still use a DTO. Be sure to annotate the Controller class with @Validated, and apply constraint annotations on the parameters.
@RequestMapping("/api/user")
@RestController
@Validated
public class UserController {
@GetMapping("{userId}")
public Result detail(@PathVariable("userId") @Min(10000000000000000L) Long userId) {
// ...
return Result.ok(userDTO);
}
}
Understanding @Valid vs. @Validated
Both @Valid and @Validated are utilized to trigger validation during request processing in Spring. However, there are key distinctions:
- Origin: @Valid is a part of the Java Bean Validation specification (JSR-303), while @Validated is specific to Spring.
- Function: @Valid validates an object, while @Validated is used for validating method parameters.
- Grouping: Only @Validated supports grouping of constraints, which is beneficial in scenarios requiring different validation rules.
Advanced Validation Techniques
#### Group Validation
In real-world applications, you may need to apply different validation rules for the same DTO based on the context (e.g., saving vs. updating). Spring Validation supports group validation for this purpose.
@Data
public class UserDTO {
@Min(value = 10000000000000000L, groups = Update.class)
private Long userId;
@NotNull(groups = {Save.class, Update.class})
@Length(min = 2, max = 10, groups = {Save.class, Update.class})
private String userName;
// Other fields...
}
@PostMapping("/save")
public Result saveUser(@RequestBody @Validated(UserDTO.Save.class) UserDTO userDTO) {
// ...
return Result.ok();
}
#### Nested Validation
In scenarios where fields are objects themselves, nested validation becomes essential. For instance, when saving user information along with job details, ensure the DTO field is annotated with @Valid.
@Data
public class UserDTO {
@Valid
private Job job;
@Data
public static class Job {
@Min(value = 1)
private Long jobId;
// Other fields...
}
}
#### Collection Validation
If your request body contains a JSON array that needs validation, wrap the collection with a custom list type and declare @Valid.
public class ValidationList implements List<UserDTO> {
@Delegate
@Valid
public List<UserDTO> list = new ArrayList<>();
}
Custom Validation
To address complex business requirements, you can create custom validations by defining constraint annotations and implementing the ConstraintValidator interface.
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EncryptIdValidator.class})
public @interface EncryptId {
String message() default "id format error";
Class[] groups() default {};
Class[] payload() default {};
}
#### Programmatic Validation
In some cases, you may want to invoke validation programmatically. Inject the javax.validation.Validator and call its API as needed.
@Autowired
private javax.validation.Validator globalValidator;
@PostMapping("/saveWithCodingValidate")
public Result saveWithCodingValidate(@RequestBody UserDTO userDTO) {
Set<ConstraintViolation<UserDTO>> validate = globalValidator.validate(userDTO, UserDTO.Save.class);
// Process validation results...
}
Enabling Fail Fast Mode
You can configure Spring Validation to implement a fail-fast mechanism that returns immediately upon a validation failure.
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
Conclusion
Understanding the principles of validation in Spring and Spring Boot is crucial for developing robust applications. By leveraging these best practices, you can enhance the security and reliability of your code significantly.
This video discusses the differences between client-side and server-side validation in Spring MVC, highlighting the importance of proper form validation.
This video explains how to implement security in Spring Boot with Azure Active Directory, including OAuth2 integration.
Thank you for reading! If you found this information helpful, please share your feedback.