雖然 Spring 標準註解(@NotBlank、@NotNull、@Min、@Size 等)涵蓋了驗證使用者輸入時的許多用例,但有時我們需要為更具體的輸入類型建立自訂驗證邏輯。在本文中,我將示範如何建立自訂註解以進行驗證。
我們需要將 spring-boot-starter-validation 依賴項新增到我們的 pom.xml 檔案中。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
讓我們建立自訂註解來驗證檔案屬性,例如檔案副檔名、檔案大小和 MIME 類型。
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint( validatedBy = {FileExtensionValidator.class} ) public @interface ValidFileExtension { String[] extensions() default {}; String message() default "{constraints.ValidFileExtension.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint( validatedBy = {FileMaxSizeValidator.class} ) public @interface ValidFileMaxSize { long maxSize() default Long.MAX_VALUE; // MB String message() default "{constraints.ValidFileMaxSize.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint( validatedBy = {FileMimeTypeValidator.class} ) public @interface ValidFileMimeType { String[] mimeTypes() default {}; String message() default "{constraints.ValidFileMimeType.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
讓我們分解這些註解的組成部分:
public class FileExtensionValidator implements ConstraintValidator<ValidFileExtension, MultipartFile> { private List<String> extensions; @Override public void initialize(ValidFileExtension constraintAnnotation) { extensions = List.of(constraintAnnotation.extensions()); } @Override public boolean isValid(MultipartFile file, ConstraintValidatorContext constraintValidatorContext) { if (file == null || file.isEmpty()) { return true; } var extension = FilenameUtils.getExtension(file.getOriginalFilename()); return StringUtils.isNotBlank(extension) && extensions.contains(extension.toLowerCase()); } }
public class FileMaxSizeValidator implements ConstraintValidator<ValidFileMaxSize, MultipartFile> { private long maxSizeInBytes; @Override public void initialize(ValidFileMaxSize constraintAnnotation) { maxSizeInBytes = constraintAnnotation.maxSize() * 1024 * 1024; } @Override public boolean isValid(MultipartFile file, ConstraintValidatorContext constraintValidatorContext) { return file == null || file.isEmpty() || file.getSize() <= maxSizeInBytes; } }
@RequiredArgsConstructor public class FileMimeTypeValidator implements ConstraintValidator<ValidFileMimeType, MultipartFile> { private final Tika tika; private List<String> mimeTypes; @Override public void initialize(ValidFileMimeType constraintAnnotation) { mimeTypes = List.of(constraintAnnotation.mimeTypes()); } @SneakyThrows @Override public boolean isValid(MultipartFile file, ConstraintValidatorContext constraintValidatorContext) { if (file == null || file.isEmpty()) { return true; } var detect = tika.detect(TikaInputStream.get(file.getInputStream())); return mimeTypes.contains(detect); } }
這些類別是 ConstraintValidator 介面的實現,包含實際的驗證邏輯。
對於 FileMimeTypeValidator,我們將使用 Apache Tika(一個旨在從多種類型的文件中提取元資料和內容的工具包)。
讓我們建立一個 TestUploadRequest 類,用於處理檔案上傳,特別是 PDF 檔案。
@Data public class TestUploadRequest { @NotNull @ValidFileMaxSize(maxSize = 10) @ValidFileExtension(extensions = {"pdf"}) @ValidFileMimeType(mimeTypes = {"application/pdf"}) private MultipartFile pdfFile; }
@RestController @Validated @RequestMapping("/test") public class TestController { @PostMapping(value = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity<String> testUpload(@Valid @ModelAttribute TestUploadRequest request) { return ResponseEntity.ok("test upload"); } }
也可以在類別層級定義自訂驗證註解來驗證類別中的欄位組合。
讓我們建立 @PasswordMatches 註解來確保類別中的兩個密碼欄位符合。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint( validatedBy = {PasswordMatchesValidator.class} ) public @interface PasswordMatches { String message() default "{constraints.PasswordMatches.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
public interface PasswordDto { String getPassword(); String getConfirmPassword(); }
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, PasswordDto> { @Override public boolean isValid(PasswordDto password, ConstraintValidatorContext constraintValidatorContext) { return StringUtils.equals(password.getPassword(), password.getConfirmPassword()); } }
PasswordDto 介面是包含密碼和確認密碼欄位的物件的介面。
PasswordMatchesValidator 類別實作 ConstraintValidator 接口,並包含用於驗證密碼和確認密碼欄位是否匹配的邏輯。
讓我們建立一個 RegisterAccountRequest 類,用於處理使用者註冊資料。
@PasswordMatches @Data public class RegisterAccountRequest implements PasswordDto { @NotBlank private String username; @NotBlank @Email private String email; @NotBlank @ToString.Exclude private String password; @NotBlank @ToString.Exclude private String confirmPassword; }
@RestController @Validated @RequestMapping("/auth") public class AuthController { @PostMapping("/register") public ResponseEntity<String> register(@RequestBody @Valid RegisterAccountRequest request) { return ResponseEntity.ok("register success"); } }
在這篇短文中,我們發現建立自訂註解來驗證欄位或類別是多麼容易。本文中的程式碼可以在我的 Github 上找到。
以上是在 Spring Boot 中建立用於驗證的自訂註釋的詳細內容。更多資訊請關注PHP中文網其他相關文章!