如何优雅地解决API请求参数中的枚举值校验

API请求参数中的枚举值校验问题,具有普遍性。

开门见山,用真实代码来进行演示!

接口请求参数

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StationStatusRequest {

        @JsonProperty("OperatorId")
        @NotEmpty(message = "运营商编码不可为空")
        @ApiModelProperty(value = "运营商ID")
        private String operatorId;

        @JsonProperty("ConnectorId")
        @NotEmpty(message = "充电接口编码不可为空")
        @ApiModelProperty(value = "充电接口编码")
        private String connectorId;

        @JsonProperty("Status")
        @EnumConstraint(value = StationStatusEnum.class, message = "充电设备接口状态非法,不在枚举范围内")
        @ApiModelProperty(value = "充电设备接口状态")
        private Integer status;

}

StationStatusEnum枚举类

接口请求参数status的值,对应于StationStatusEnum枚举类的code!

/**
 * 充电设备接口状态 0:离网,1:空闲,2:占用
 * 
 * @author xuej
 * 
 */
@Getter
@AllArgsConstructor
public enum StationStatusEnum {

        OFF_LINE(0, "离网"),

        IDLE(1, "空闲"),

        OCCUPIED(2, "占用"),

        ;

        /**
         * 异常码
         */
        private final Integer code;

        /**
         * 异常信息
         */
        private final String message;

        /**
         * 解析异常码
         * 
         * @param code
         * @return
         */
        public static StationStatusEnum parse(Integer code) {
                StationStatusEnum e = resolve(code);
                if (e == null) {
                        throw new IllegalArgumentException("当前异常码不存在 [" + code + "]");
                }
                return e;
        }

        /**
         * 转化异常码
         * 
         * @param code
         * @return
         */
        private static StationStatusEnum resolve(Integer code) {
                for (StationStatusEnum e : values()) {
                        if (Objects.equals(e.code, code)) {
                                return e;
                        }
                }
                return null;
        }
}

自定义注解@EnumConstraint

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
@Constraint(validatedBy = EnumConstraintValidator.class)
public @interface EnumConstraint {
        Class<?> value();

        String message() default "参数不在枚举范围内";

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

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

自定义注解@EnumConstraintValidator

@Slf4j
public class EnumConstraintValidator implements ConstraintValidator<EnumConstraint, Object>, Annotation {
        private List<Object> values = new ArrayList<>();

        @Override
        public void initialize(EnumConstraint enumValidator) {
                Class<?> clz = enumValidator.value();
                Object[] ojects = clz.getEnumConstants();
                try {
                        Method method = clz.getMethod("getCode");
                        if (Objects.isNull(method)) {
                                throw new Exception(String.format("枚举对象%s缺少字段code", clz.getName()));
                        }
                        Object value = null;
                        for (Object obj : ojects) {
                                value = method.invoke(obj);
                                values.add(value);
                        }
                } catch (Exception e) {
                        log.error("枚举校验初始化处理失败,原因 = {},", Throwables.getStackTraceAsString(e));
                }
        }

        @Override
        public Class<? extends Annotation> annotationType() {
                return null;
        }

        @Override
        public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
                return Objects.isNull(value) || values.contains(value);
        }
}