SpringBoot参数验证高阶技巧


SpringBoot参数验证高阶技巧

1 基本注解

Spring Validation 提供了一组用于常见验证任务的标准如下示例:

在Controller接口参数上开启验证功能:

2 自定义注解验证

对于特定的业务规则,你可以创建自己的自定义验证约束。

public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {  
    @Resource  
    private UserRepository userRepository;  
    @Override  
    public boolean isValid(String username, ConstraintValidatorContext context) {    
        if (username == null) {      
            return true; // Let @NotNull handle null values    
        }    
        return !userRepository.existsByUsername(username);  
    }
}

3 分组验证

分组验证允许你针对不同场景应用不同的规则,例如创建操作与更新操作。如下示例(定义分组):

使用分组

public class ProductDTO {  
    @Null(groups = ValidationGroups.Create.class, message = "创建商品时ID必须为空")  
    @NotNull(groups = ValidationGroups.Update.class, message = "更新商品时商品ID不能为空")  
    private Long id;  
    @NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class})  
    private String name;
}

分组验证

注意:使用 @Validated 注解,因为它是支持验证组的注解。**@Valid** 不支持。

4 嵌套验证

对于复杂对象,你可以验证嵌套对象和集合。

注意:使用 @Valid 注解需要级联验证的字段,以确保验证器深入检查嵌套对象。

5 方法级验证

你也可以直接对服务层方法进行验证,而不仅仅是对控制器参数进行验证。

如果你是在Controller方法上那么你不需要在类上使用@Validated注解。

6 错误消息国际化

当发生错误时我们可以通过2中方式来提示具体的错误消息。

  • 直接指定错误消息

直接通过 message 指定要展示的错误消息。

  • 使用占位符

message属性可以使用 {xxx} 语法,其中 xxx 为你在资源文件中定义的key。

接下来,新建 classpath:messages_zh_CN.properties 和 classpath:messages_en_US.properties 资源文件。

上面我们是使用的spring boot默认的basename,你可以自定义,通过 spring.messages.basename 属性配置。

7 编程验证

除了注解之外,你还可以手动触发验证。

适用于验证是有条件的复杂业务逻辑,或用于验证未通过控制器传递的对象。

8 组合验证

将多个基本约束组合成一个单一的、可复用的且更具表现力的注解。

@NotNull@Size(min = 8, max = 30)
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$",
         message = "密码必须包含至少一个数字、小写字母、大写字母和特殊字符")
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
public @interface StrongPassword {
    String message() default "密码不符合安全要求";  
    Class<?>[] groups() default {};  
    Class<? extenjavads Payload>[] payload() default {};
}

使用组合注解

优点:提高代码可读性,并确保验证规则得到一致应用。

9 跨字段验证

根据字段与其他字段的关系来验证字段,例如确保 “确认密码” 字段与 “密码” 字段匹配。创建一个类级约束:

密码验证器

public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {  
    private String field;  
    private String fieldMatch;  
    @Override  
    public void initialize(PasswordMatches constraintAnnotation) {    
        this.field = constraintAnnotation.field();    
        this.fieldMatch = constraintAnnotation.fieldMatch();  
    }  
    @Override  
    public boolean isValid(Object value, ConstraintValidatorContext context) {    
        try {      
            // 通过反射获取字段值      
            Field field1 = value.getClass().getDeclaredField(field);      
            field1.setAccessible(true);      
            Object fieldValue1 = field1.get(value);      
            Field field2 = value.getClass().getDeclaredField(fieldMatch);      
            field2.setAccessible(true);      
            Object fieldValue2 = field2.get(value);      
            // 比较两个字段值(处理null情况)      
            if (fieldValue1 == null) {        
                return fieldValue2 == njavaull;      
            }      
            return fieldValue1.equals(fieldValue2);    
        } catch (NoSuchFieldException | IllegalAccessException e) {     
            throw new RuntimeException("Failed to validate password fields", e);    
        }  
    }
}

使用注解

Controller接口

@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody UserRegistrationDTO dto, BindingResult result) {
  if (result.hasErrors()) {
    List<String> errors = result.getAllErrors()
        .stream()
        .map(err -> err.getDefaultMessage())
        .toList() ;
    return ResponseEntity.badRequest().body(errors);
  }
  return ResponseEntity.ok("Registration successful");
}

文章作者: 威@猫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 威@猫 !
评论
  目录