GroupSequenceProvider动态校验入参;@Validated和@Valid的区别;自定义校验注解;控制注解校验顺序
GroupSequenceProvider动态校验入参
需求场景
平时在controller的入参校验中大多都是单个参数校验
一般都是直接用javax的注解 + javax.validation.Valid注解一把梭。
不过也有场景是需要动态校验。
假设入参的年龄和地区相关。
上海地区的年龄范围需要在10-20
浙江地区的年龄范围需要在10-30
这个时候要么就是直接硬编码了,但是前人肯定遇到过这种问题。
org.hibernate.validator.group.GroupSequenceProvider注解就能帮我们稍微实现的漂亮一点。
如果当前项目中没有对应依赖的话先引入依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
代码Demo
自定义编码实现动态校验
package com.xxx.xxx.valid.group;
/**
* 根据动态条件来决定是否判断 NotNull
*/
public interface NotNullByConditionGroup {
}
package com.xxx.xxx.xxx;
import com.xxx.xxx.valid.group.NotNullByConditionGroup;
import com.xxx.xxx.valid.ProductAccessoriesSkuQueryProvider;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.group.GroupSequenceProvider;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@GroupSequenceProvider(ProductAccessoriesSkuQueryProvider.class)
@Data
public class ProductAccessoriesSkuQueryListDTO implements Serializable {
private static final long serialVersionUID = 8297881653432975830L;
@ApiModelProperty(value = "产品id")
@NotNull(message = "产品id不能为空", groups = NotNullByConditionGroup.class)
private Long productId;
@ApiModelProperty(value = "颜色id,详情见数据字典 color")
@NotNull(message = "颜色id不能为空", groups = NotNullByConditionGroup.class)
private Long colorId;
@ApiModelProperty(value = "尺寸id,详情见数据字典 sales_size")
@NotNull(message = "尺寸id不能为空", groups = NotNullByConditionGroup.class)
private Long sizeId;
@ApiModelProperty(value = "产品skuId")
private Long productSkuId;
}
package com.xxx.xxx.valid;
import com.xxx.xxx.xxx.ProductAccessoriesSkuQueryListDTO;
import com.xxx.xxx.xxx.group.NotNullByConditionGroup;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 校验当 productSkuId 不存在的时候 sizeId + productId + colorId 不能为空
*/
public class ProductAccessoriesSkuQueryProvider implements DefaultGroupSequenceProvider<ProductAccessoriesSkuQueryListDTO> {
@Override
public List<Class<?>> getValidationGroups(ProductAccessoriesSkuQueryListDTO bean) {
List<Class<?>> defaultGroupSequence = new ArrayList<>();
defaultGroupSequence.add(ProductAccessoriesSkuQueryListDTO.class); // 这一步不能省,否则Default分组都不会执行了,会抛错的
if(Objects.nonNull(bean)){
Long productSkuId = bean.getProductSkuId();
if(Objects.isNull(productSkuId)){
defaultGroupSequence.add(NotNullByConditionGroup.class);
}
}
return defaultGroupSequence;
}
}
@PostMapping("queryList")
public CommonResult<List<? extends xxx>> queryList(@RequestBody @Validated ProductAccessoriesSkuQueryListDTO queryList){
return CommonResult.success();
}
@Validated指定分组校验
public class ValidatedGroup {
public interface CREATE{}
public interface DELET{}
public interface UPDATE{}
public interface QUERY{}
}
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class ValidatedVO {
//更新、删除时不能为空
@NotNull(message = "id不能为空", groups = {ValidatedGroup.UPDATE.class, ValidatedGroup.DELET.class})
private Long id;
//新增、更新时不能为空
@NotBlank(message = "name不能为空", groups = {ValidatedGroup.CREATE.class ,ValidatedGroup.UPDATE.class})
private String name;
//查询时不能为空
@NotBlank(message = "queryParam不能为空", groups = {ValidatedGroup.QUERY.class})
private String queryParam;
}
public ValidatedVO bindValidate(@RequestBody @Validated(value= {ValidatedGroup.DELET.class}) ValidatedVO validatedVO/*, BindingResult result*/) {
return validatedVO;
}
@Validated和@Valid的区别
在校验的时候会发现有2个很类似的注解,@Valid和@Validated
首先这两个的包都是不同的javax.validation.Validorg.springframework.validation.annotation.Validated
其次@Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验@Valid可以用在属性级别约束,用来表示级联校验。@Valid可用于方法、字段、构造器和参数上
@Validated:Spring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。上面提到的分组校验就是利用了其分组功能@Validated注解可以用于类级别,用于支持Spring进行方法级别的参数校验。@Validated只能用在类、方法和参数上;
自定义校验注解
定义一个校验注解
package com.xx.xx.xx;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//这里指向的是我们自定义的校验规则,可以治党多个不同校验器,适配不同的情况
@Constraint(validatedBy = {DataCheckVaildator.class})
public @interface DataCheck {
//当然这里的message也可以去属性文件中指定
String message() default "请提交规范数据";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
//用来获取注解属性值
int[] vals() default {};
}
在定义个注解校验器
需要声明校验的注解和被校验的数据类型
package com.xxx.xxx.xxx;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class DataCheckVaildator implements ConstraintValidator<DataCheck, Long> {
private Set<Integer> setNum = new HashSet<>();
@Override
public void initialize(DataCheck constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
setNum.add(val);
}
}
@Override
public boolean isValid(Long value, ConstraintValidatorContext context) {
if(Objects.isNull(value)){
return true;
}
return setNum.contains(value);
}
}
使用
@ApiModelProperty(value = "产品skuId")
@DataCheck(vals = {1,2,3}, message = "产品skuId不符")
private Long productSkuId;
//这里使用spring的org.springframework.validation.annotation.Validated
//和jsr的javax.validation.Valid
//都可以
@PostMapping("queryList")
public CommonResult<List<? extends ProductAccessoriesSkuListVO>> queryList(@RequestBody @Validated ProductAccessoriesSkuQueryListDTO queryList){
return CommonResult.success();
}
控制注解校验顺序
有些时候需要在前一个校验通过后再校验,这个时候就需要控制一下校验顺序了
可以使用javax.validation.GroupSequence来定义顺序,然后声明这个顺序接口class就行了
上代码demo
定义校验顺序
package com.xxx.xxx.xxx.xxx;
import javax.validation.GroupSequence;
import javax.validation.groups.Default;
/**
* 定义校验顺序
*/
@GroupSequence({Default.class, TaxPriceCalculateValidOrderGroup.TaxPriceCalculateGroup.class})
public interface TaxPriceCalculateValidOrderGroup {
interface TaxPriceCalculateGroup {};
}
使用分组
public CommonResult<?> edit(@RequestBody @Validated(value = TaxPriceCalculateValidOrderGroup.class) ProductDTO productDTO){
xxxx
在目标类上使用注解
@TaxPriceCalculate(message = "含税价-不含税价与比例不符", groups = TaxPriceCalculateValidOrderGroup.TaxPriceCalculateGroup.class)
public class SkuSupplierBaseInfoDTO {
xxx
更多推荐




所有评论(0)