Jakarta Bean Validation
後端開發 API 時,常需要對 Request 欄位進行條件檢核,例如:Not NULL、僅限數字、長度限制等。 尤其在串接資料庫時,欄位檢核更是必要的。
Node.js Express 開發常搭配 Express-Validator middleware 來簡化資料欄位基本驗證的程式碼, 在 Spring Boot 中,
Jakarta Bean Validation提供了類似且更強大的功能。
Overview
Jakarta Bean Validation (前身為 Java Bean Validation) 是 Java EE/Jakarta EE 的標準規範,用於驗證 Java Bean 的屬性。Spring Boot 內建支援此規範,透過註解方式可以輕鬆實現資料驗證。
主要特色
- 註解驅動:使用簡潔的註解進行驗證規則定義
- 內建驗證器:提供豐富的內建驗證約束
- 自訂驗證:支援自訂驗證邏輯
- 國際化支援:錯誤訊息支援多語言
- 群組驗證:支援不同情境下的驗證規則
Jakarta Bean Validation Constraints
Built-In Jakarta Bean Validation Constraints 都很實用. Ref: Introduction to Jakarta Bean Validation :: Jakarta EE Tutorial :: Jakarta EE Documentation.
| Constraint | Description | Example |
|
| The value of the field or property must be false. |
|
|
| The value of the field or property must be true. |
|
|
| The value of the field or property must be a decimal value lower than or equal to the number in the value element. |
|
|
| The value of the field or property must be a decimal value greater than or equal to the number in the value element. |
|
|
| The value of the field or property must be a number within a specified range. The integer element specifies the maximum integral digits for the number, and the fraction element specifies the maximum fractional digits for the number. |
|
|
| The value of the field or property must be a valid email address. |
|
|
| The value of the field or property must be a date in the future. |
|
|
| The value of the field or property must be a date or time in present or future. |
|
|
| The value of the field or property must be an integer value lower than or equal to the number in the value element. |
|
|
| The value of the field or property must be an integer value greater than or equal to the number in the value element. |
|
|
| The value of the field or property must be a negative number. |
|
|
| The value of the field or property must be negative or zero. |
|
|
| The value of the field or property must contain at least one non-white space character. |
|
|
| The value of the field or property must not be empty. The length of the characters or array, and the size of a collection or map are evaluated. |
|
|
| The value of the field or property must not be null. |
|
|
| The value of the field or property must be null. |
|
|
| The value of the field or property must be a date in the past. |
|
|
| The value of the field or property must be a date or time in the past or present. |
|
|
| The value of the field or property must match the regular expression defined in the regexp element. |
|
|
| The value of the field or property must be a positive number. |
|
|
| The value of the field or property must be a positive number or zero. |
|
|
| The size of the field or property is evaluated and must match the specified boundaries. If the field or property is a String, the size of the string is evaluated. If the field or property is a Collection, the size of the Collection is evaluated. If the field or property is a Map, the size of the Map is evaluated. If the field or property is an array, the size of the array is evaluated. Use one of the optional max or min elements to specify the boundaries. |
|
Spring Boot 整合使用
基本用法
在 Spring Boot 中使用 Jakarta Bean Validation 非常簡單:
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<String> createUser(@Valid @RequestBody User user) {
// 驗證通過後的處理邏輯
return ResponseEntity.ok("User created successfully");
}
}
DTO 類別定義
public class User {
@NotBlank(message = "使用者名稱不能為空")
@Size(min = 3, max = 20, message = "使用者名稱長度必須在 3-20 字元之間")
private String username;
@Email(message = "請輸入有效的電子郵件地址")
@NotBlank(message = "電子郵件不能為空")
private String email;
@Min(value = 18, message = "年齡必須大於等於 18")
@Max(value = 120, message = "年齡必須小於等於 120")
private Integer age;
// getters and setters
}
錯誤處理
@ControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
}
進階功能
群組驗證
public interface CreateGroup {}
public interface UpdateGroup {}
public class User {
@NotNull(groups = UpdateGroup.class)
private Long id;
@NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
private String username;
}
// Controller 中使用
@PostMapping("/users")
public ResponseEntity<String> createUser(@Validated(CreateGroup.class) @RequestBody User user) {
// 建立使用者邏輯
}
自訂驗證器
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface PhoneNumber {
String message() default "無效的電話號碼格式";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String> {
@Override
public boolean isValid(String phoneNumber, ConstraintValidatorContext context) {
return phoneNumber != null && phoneNumber.matches("^09\\d{8}$");
}
}