SpringBoot入参字段校验-分组
背景: 有一个请求实体bean,里面的属性在有些接口里面用,有些是在另外一个接口用;
public class FantasyProductUpDownRequest {
// 批量接口参数用
private Set<Long> skuStoreIdSet;
// 单个接口用
private Long skuStoreId;
}
因为是相同功能的参数,所以就写在一个实体bean里面了,之所以要分组,
是因为无论是批量接口还是单个接口都需要对应的参数不能为空,
也就是说调用批量接口的时候skuStoreIdSet要添加不能为空校验,
调用单个接口的时候skuStoreId要添加不能为空校验,
public class FantasyProductUpDownRequest {
@ApiModelProperty(value = "门店商品id列表-批量接口用", dataType = "List")
@NotEmpty(message = "lang.middleend.product.paramMust")
private Set<Long> skuStoreIdSet;
@ApiModelProperty(value = "门店商品id-单个接口用", dataType = "Long")
@NotNull(message = "lang.middleend.product.paramMust")
private Long skuStoreId;
}
如果这样直接都加上的话,会出现一个问题,
比如在调用批量接口的时候,你肯定只会传进去skuStoreIdSet参数,
skuStoreId一定会为空,会非空校验不过从而报错,
相应的调用单个接口的时候,你肯定只会传进去skuStoreId参数,
skuStoreIdSet一定会为空,会非空校验不过从而报错,
那如何做,才能让我们在调用font color=“red”>批量接口的时候,只有skuStoreIdSet参数的
非空校验会生效,skuStoreId非空校验不生效呢?答案就是分组,如下:
public class FantasyProductUpDownRequest {
@ApiModelProperty(value = "门店商品id列表-批量接口用", dataType = "List")
@NotEmpty(message = "lang.middleend.product.paramMust", groups = FantasyProductUpDownRequestGroup.Batch.class)
private Set<Long> skuStoreIdSet;
@ApiModelProperty(value = "门店商品id-单个接口用", dataType = "Long")
@NotNull(message = "lang.middleend.product.paramMust", groups = FantasyProductUpDownRequestGroup.Single.class)
private Long skuStoreId;
}
于是我们在skuStoreIdSet和skuStoreId两个参数的非空约束的groups参数设置了分组接口,也就是设置了groups属性,
groups的值要求必须是一个类对象,一般可以通过创建多个空接口来进行分组,
如果分组较多可以将这些用以校验分组的接口放在同一个类下进行管理:
public class FantasyProductUpDownRequestGroup {
public interface Batch{}
public interface Single{}
}
在接口应用的时候,你让哪个参数的校验生效,只需要在@Validated里面设置和对应参数的
groups相同的分组接口,如下:
// 批量接口
public RestResult<ProcessResult> batchDown(@RequestBody
@Validated({FantasyProductUpDownRequestGroup.Batch.class}) FantasyProductUpDownRequest fantasyProductUpDownRequest)
// 单个接口
public RestResult<ProcessResult> up(@RequestBody
@Validated({FantasyProductUpDownRequestGroup.Single.class}) FantasyProductUpDownRequest fantasyProductUpDownRequest)
问题:如果想要让两个不同分组接口参数的约束在一个接口里面都生效怎么办?
解决方案:你就在@Validated里面把两个参数的分组接口都写上,如下:
// 批量接口
public RestResult<ProcessResult> batchDown(@RequestBody
@Validated({FantasyProductUpDownRequestGroup.Batch.class, FantasyProductUpDownRequestGroup.Single.class,}) FantasyProductUpDownRequest fantasyProductUpDownRequest)
问题:如果FantasyProductUpDownRequest里面再多一个参数,并且这个参数是必传的,如下:
public class FantasyProductUpDownRequest {
@ApiModelProperty(value = "门店商品id列表-批量接口用", dataType = "List")
@NotEmpty(message = "lang.middleend.product.paramMust", groups = FantasyProductUpDownRequestGroup.Batch.class)
private Set<Long> skuStoreIdSet;
@ApiModelProperty(value = "门店商品id-单个接口用", dataType = "Long")
@NotNull(message = "lang.middleend.product.paramMust", groups = FantasyProductUpDownRequestGroup.Single.class)
private Long skuStoreId;
@NotBlank(message = "门店编码为空")
private String storeCode;
}
如果此时你的接口调用如下:
// 批量接口
public RestResult<ProcessResult> batchDown(@RequestBody
@Validated({FantasyProductUpDownRequestGroup.Batch.class}) FantasyProductUpDownRequest fantasyProductUpDownRequest)
此时它只会对skuStoreIdSet做校验,而不会对storeCode校验,因为storeCode没有设置分组接口,
解决方案:没有指明分组的属性都属于Default,所以分组接口继承Default就可以解决, 如下:
public class FantasyProductUpDownRequestGroup {
public interface Batch extends Default {}
}
所以上面的问题:如果想要让两个不同分组接口参数的约束在一个接口里面都生效怎么办?
也可以通过接口继承解决。如下:
public class FantasyProductUpDownRequestGroup {
public interface Batch {}
public interface Single{}
public interface All extends Default, Batch, Single{}
}
在应用的时候如下:
public RestResult<ProcessResult> up(@RequestBody @Validated({FantasyProductUpDownRequestGroup.All.class}) FantasyProductUpDownRequest fantasyProductUpDownRequest)
自定义校验注解
背景:需要按照字节约束用户的输入,现有的约束不能满足,就只能自定义
先定义注解ByteSize
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
// 指定验证器,来验证用户输入是否满足自己的规则
@Constraint(validatedBy = ByteSizeValidator.class)
public @interface ByteSize {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
自定义验证器 ByteSizeValidator
// 实现ConstraintValidator接口泛型第一个ByteSize指定该验证器验证的注解,第二个String是用户输入的数据类型
public class ByteSizeValidator implements ConstraintValidator<ByteSize, String> {
private ByteSize byteSize;
@Override
public void initialize(ByteSize constraintAnnotation) {
this.byteSize = constraintAnnotation;
}
// 编写自己的验证规则
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value != null) {
// 不允许纯空格
if (value.getBytes(Charset.forName("GBK")).length > 0 && value.trim().getBytes(Charset.forName("GBK")).length == 0 ) {
return false;
} else {
if (value.getBytes(Charset.forName("GBK")).length <= byteSize.max() && byteSize.min() <= value.getBytes(Charset.forName("GBK")).length) {
return true;
} else {
return false;
}
}
} else {
if (byteSize.min() > 0) {
return false;
} else {
return true;
}
}
}
}
应用
// 不能超过1500个字节
@ByteSize(min = 0, max = 1500)
private String description;