虽然 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中文网其他相关文章!