在项目开发中经常会遇到使用枚举值校验的功能,而使用 validation 自带的校验功能较为单一,为了简化开发,需要一个自定义的枚举校验器

1. 注解及枚举校验器

@NotNull
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValue.Validator.class)
public @interface EnumValue {

    String message() default "无效的值";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    Class<? extends Enum<?>> enumClass();

    String enumMethod() default "isValid";

    class Validator implements ConstraintValidator<EnumValue, Object> {

        private Class<? extends Enum<?>> enumClass;
        private String enumMethod;

        @Override
        public void initialize(EnumValue enumValue) {
            enumMethod = enumValue.enumMethod();
            enumClass = enumValue.enumClass();
        }

        @Override
        public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
            if (value == null) {
                return Boolean.TRUE;
            }
            if (enumClass == null || enumMethod == null) {
                return Boolean.TRUE;
            }

            Class<?> valueClass = value.getClass();
            Method method;
            try {
                method = enumClass.getDeclaredMethod(enumMethod, valueClass);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(String.format("%s.%s 不存在", enumClass.getName(), enumMethod));
            }

            if (!Boolean.TYPE.equals(method.getReturnType()) && !Boolean.class.equals(method.getReturnType())) {
                throw new RuntimeException(String.format("%s.%s 返回值不是布尔类型", enumClass.getName(), enumMethod));
            }
            if (!Modifier.isStatic(method.getModifiers())) {
                throw new RuntimeException(String.format("%s.%s 不是静态方法", enumClass.getName(), enumMethod));
            }

            Boolean result;
            try {
                method.setAccessible(true);
                result = (Boolean) method.invoke(null, value);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(String.format("%s.%s 调用失败:%s", enumClass.getName(), enumMethod, e.getMessage()));
            }
            return result != null && result;
        }
    }
}

2. 测试

@AllArgsConstructor
@Getter
public enum UserStatusEnum {

    DISABLE(0, "禁用"),
    ENABLE(1, "启用"),
    ;

    private int code;
    private String value;

    private static boolean isValid(Integer code) {
        return Arrays.stream(UserStatusEnum .values()).anyMatch(e -> e.getCode() == code);
    }
}
@ApiModel("UserParam对象")
public class UserParam {

    @EnumValue(enumClass = UserStatusEnum.class)
    @ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用")
    private Integer status;
}
@Validated
@RestController
@RequestMapping("/user")
public class ResourceCategoryController {

    @ApiOperation("更新用户状态 字段")
    @PutMapping("/updateStatus/{id}")
    public ServerResponse updateStatus(@PathVariable Long userId,
                                       @RequestParam @EnumValue(enumClass = UserStatusEnum.class) Integer status) {
        return ServerResponse.createBySuccess();
    }

    @ApiOperation("更新用户状态 对象")
    @PutMapping("/updateStatus/{id}")
    public ServerResponse updateStatus(@PathVariable Long userId,
                                       @RequestBody @Validated UserParam userParam) {
        return ServerResponse.createBySuccess();
    }
}

返回结果:

{
  "status": 100110,
  "msg": "updateStatus.status: 无效的值"
}

Q.E.D.


盛年不重来,一日难再晨。