文章目录
- 背景
- 解决
- 实践
- 定义枚举类 InEnum注解
- 定义验证逻辑 InEnumValidator
- 实际使用
背景
业务要做电商平台做入参, 在电商平台被抽离成枚举类的情况下 ,要怎么验证输入的参数是正确的呢?
解决
Constraint 实现自定义验证逻辑
@Constraint 注解用于标注其他注解,将其声明为验证约束。自定义约束注解需要指定一个或多个验证器实现类。
主要属性
validatedBy(): 指定实现约束验证逻辑的类(一个或多个)。这些类必须实现 ConstraintValidator 接口
message(): 定义默认的错误消息(通常使用 {} 占位符从资源文件中获取)
groups(): 允许指定验证分组
payload(): 可以附加到约束上的额外负载信息
实践
定义枚举类 InEnum注解
用来收纳验证用的基础信息, 包含要验证的枚举类,枚举类的取值方法, 报错提示
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;@Documented
@Constraint(validatedBy = {InEnumValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface InEnum {// 错误提示信息String message() default "必须在指定范围 {value}";// 分组Class<?>[] groups() default {};// 负载Class<? extends Payload>[] payload() default {};// 指定枚举类Class<? extends Enum<?>> enumClass();// 枚举中用于比较的方法名(默认为name())String enumMethod() default "name";// 是否忽略大小写boolean ignoreCase() default false;// 新增:是否在错误消息中显示所有合法值boolean showValues() default true;
}
定义验证逻辑 InEnumValidator
实现 ConstraintValidator接口
- initialize(A constraintAnnotation) 用于初始化验证器,用来获取InEnum注解上的数据
- isValid(T value, ConstraintValidatorContext context) 验证逻辑 true验证通过,false验证失败, 入参context处理错误信息, value 是要验证的对象
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {/*** 枚举验证器实现类,用于验证输入值是否匹配指定枚举类中的值*/private Class<? extends Enum<?>> enumClass; // 要验证的枚举类private String enumMethod; // 枚举类中用于获取值的方法名private boolean ignoreCase; // 是否忽略大小写进行验证private boolean showValues; // 验证失败时是否显示所有合法值private List<String> validValues = new ArrayList<>(); // 预加载的所有合法值列表/*** 初始化验证器* @param constraintAnnotation 包含验证配置的注解实例* @throws RuntimeException 如果初始化过程中发生反射相关异常*/@Overridepublic void initialize(InEnum constraintAnnotation) {enumClass = constraintAnnotation.enumClass();enumMethod = constraintAnnotation.enumMethod();ignoreCase = constraintAnnotation.ignoreCase();showValues = constraintAnnotation.showValues();// 通过反射获取枚举值并预加载到validValues集合中try {Method method = enumClass.getMethod(enumMethod);for (Enum<?> enumConstant : enumClass.getEnumConstants()) {Object value = method.invoke(enumConstant);if (value != null) {validValues.add(value.toString());}}} catch (Exception e) {throw new RuntimeException("无法初始化枚举验证器", e);}}/*** 验证输入值是否有效* @param value 要验证的输入值* @param context 验证上下文,用于自定义错误消息* @return true如果验证通过,false如果验证失败* @throws RuntimeException 如果验证过程中发生反射相关异常*/@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {if (value == null) {return true;}try {Method method = enumClass.getMethod(enumMethod);// 遍历枚举值进行匹配验证for (Enum<?> enumConstant : enumClass.getEnumConstants()) {Object enumValue = method.invoke(enumConstant);if (enumValue == null) {continue;}// 根据ignoreCase设置进行不同方式的比较if (ignoreCase && value instanceof String && enumValue instanceof String) {if (((String) enumValue).equalsIgnoreCase((String) value)) {return true;}} else if (Objects.equals(enumValue, value)) {return true;}}// 验证失败时处理错误消息if (showValues) {String validValuesStr = validValues.stream().collect(Collectors.joining(", "));context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate().replace("{value}", validValuesStr)).addConstraintViolation();}return false;} catch (Exception e) {throw new RuntimeException(e);}}
}
实际使用
// 1.0 定义枚举类
@Getter
@AllArgsConstructor
public enum PlatformTypeEnum {/*** 未来支持的平台很多,但是现在只支持抖音* */DY(1, "dy", "抖音"),;private final Integer value;private final String code;private final String name;public String getCode(){return code;}
}// 2.0 验证入参
@Data
public class CrowdReqVO {@NotNull(message = "抖音号不能为空")@ApiModelProperty("抖音号")private String awemeId;@DateTimeFormat(pattern = "yyyy-MM-dd")@ApiModelProperty("查询日期,格式:yyyy-MM-dd")@NotNull(message = "查询日期不能为空")private LocalDate searchDate;@ApiModelProperty("直播间类型:dy-抖音;ks-快手;sph-视频号")@NotEmpty(message = "请输入平台类型")@InEnum(enumClass = PlatformTypeEnum.class, enumMethod = "getCode", message = "平台类型必须在指定范围: {value}")private String platformType;
}