Hi,大家好。
今天实现一个枚举类型的参数校验器。
效果图:
一个接口在Controller层,如果有个属性是枚举类型,我们把参数定义为String呢,还是定义为枚举?
类型 | 优点 | 缺点 |
---|---|---|
String | 方便灵活,想传啥传啥 | 其他开发看到此代码不明确可以传什么值 |
枚举 | 非常明确知道可以传哪些值 | 不好做参数校验,容易报HttpMessageNotReadableException类型的异常 |
项目地址:还是在之前的参数校验项目基础上开发的
git@github.com:cmhhcm/SecurityTest.git
postman接口导入连接:
https://www.getpostman.com/collections/d221c48fd9097d869db4
在 springboot_mybatis_curd目录下
这里是要对枚举类型参数进行校验,如果是直接定义为枚举,类似这样:
private BookStatusEnum bookStatus;
前端调接口如果传的值不是枚举所包含的字符串,就会报JSON转换错误,因为这个字符串没法转成对应的枚举属性的字符串类型。
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `com.cmh.eurekaclient2.constant.BookStatusEnum` from String "fdaf": not one of the values accepted for Enum class: [AVAILABLE, BORROWED, NO_PURCHASED, PURCHASING]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.cmh.eurekaclient2.constant.BookStatusEnum` from String "fdaf": not one of the values accepted for Enum class: [AVAILABLE, BORROWED, NO_PURCHASED, PURCHASING]
at [Source: (PushbackInputStream); line: 2, column: 17] (through reference chain: com.cmh.eurekaclient2.value.BookQueryCondition["bookStatus"])
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:285)
解决办法:用String类型来表示。这样自定义的校验器就可以用上了。但是总感觉这么解决不够优雅。笔者本意还是希望定义成枚举类型的,然后直接在枚举类型上加校验。
代码:
1、定义注解
package com.cmh.eurekaclient2.annotation;
import com.cmh.eurekaclient2.validation.EnumValidation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {EnumValidation.class})
public @interface EnumValidator {
boolean canNull() default true;
Class<?> value();
String message() default "enum val is illegal";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2、自定义校验器
package com.cmh.eurekaclient2.validation;
import com.cmh.eurekaclient2.annotation.EnumValidator;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class EnumValidation implements ConstraintValidator<EnumValidator, Object> {
private List<Object> values = new ArrayList<>();
private boolean canNull;
@Override
public void initialize(EnumValidator enumValidator) {
Class<?> clazz = enumValidator.value();
canNull = enumValidator.canNull();
Object[] enumConstants = clazz.getEnumConstants();
Arrays.stream(enumConstants).forEach(e -> values.add(e.toString()));
}
@Override
public boolean isValid(Object val, ConstraintValidatorContext constraintValidatorContext) {
if (canNull) {
return true;
}
if (val instanceof String) {
String str = (String) val;
return StringUtils.isNotEmpty(str) && values.contains(str);
}
return !Objects.isNull(val) && values.contains(val);
}
}
3、使用
@PostMapping("book/list")
public ResponseVO getBooks(@RequestBody @Validated BookQueryCondition condition) {
return ResponseVO.success(bookService.getBooks(condition));
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookQueryCondition {
private String name;
private Set<Integer> ids;
@EnumValidator(canNull = false,value = BookStatusEnum.class)
private String bookStatus;
}
好了,再会!下期抽空写一个文件类型的校验器,主要校验文件大小和文件类型。