这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战
前言
在我们应用程序的业务逻辑中,经常会碰到参数校验的情况,手动的在代码层上面进行校验就会带来很不好的体验,验证代码繁琐,方法内代码显得冗长且和业务逻辑关系不大,维护的成本较高。因此有了这个Java Validation API(JSR 303)。而我们使用的Hibernate Validator是JSR 303标准的一个具体实现,用于对参数进行合法性校验。
使用
(一) 集成
springboot 2的web'项目中默认集成spring-boot-starter-validation
,直接使用即可
其他版本可能需要添加validation-api
,hibernate-validator
依赖
(二) 使用
1. 注解使用
在Bean类中的需要的属性字段上添加对应的注解
@Data
public class StudentGroupDto {
//主键id
@Null(message = "id必须为空", )
private Long id;
//姓名
@NotEmpty(message = "姓名不能为空")
@Length(min =2, message = "姓名长度应大于{min}位")
@Length(max =20, message = "姓名长度应小于{max}位")
private String name;
}
在controller证明需要验证的bean
@RequestMapping("/save")
public ResultVo save(@Validated StudentGroupDto studentGroupDto) {
return ResultVo.success();
}
在请求的时候如果参数验证不通过则直接抛出BindException
异常,这个异常我们通常不会直接返回到前端,需要在全局异常捕获中处理成,我们需要的格式,处理方法
@ExceptionHandler(value = org.springframework.validation.BindException.class)
public ResultVo bindExceptionHandle(BindException ex){
log.info("用户数据参数异常: {}",ex);
StringBuffer defaultMessage = new StringBuffer("参数异常");
List<ObjectError> list = ex.getBindingResult().getAllErrors();
if(list!=null && list.size()>0){
defaultMessage.append(":") ;
try {
int i=0;
for (ObjectError error : list) {
if(i>0){ defaultMessage.append(","); }
defaultMessage.append(error.getDefaultMessage());
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return ResultVo.fail(defaultMessage.toString());
}
2. 方法调用使用
构造validator对象
private static validator = Validation.buildDefaultValidatorFactory().getValidator();
public static void validateEntity(Object object, Class<?>... groups)
throws RRException {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
StringBuilder msg = new StringBuilder();
for(ConstraintViolation<Object> constraint: constraintViolations){
msg.append(constraint.getMessage()).append("<br>");
}
throw new Exception(msg.toString());
}
}
调用validateEntity
方法验证,如果验证错误直接抛出异常
(三) 约束条件解释
在idea的BeanValidation中的constraints能看到所有的约束条件
注解 | 解释 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@NotBlank 验证字符串非null,且长度必须大于0 | |
@NotEmpty | 被注释的元素的必须非空 |
@Length(min=,max=) | 被注释的字符串的大小必须在指定的范围内,如果为null不验证 |
@Range(min=,max=,message=) | 被注释的元素必须在合适的范围内 |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max=, min=) | 被注释的元素的大小必须在指定的范围内 |
被注释的元素必须是电子邮箱地址 | |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@FutureOrPresent 判断日期是否是将来或现在日期 | |
@Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
(四) 验证组的使用
在不同的业务中,对于一个bean中需要的字段可能不同,如果只是使用默认的验证组Default.class(不添加groups 就是使用的Default.class)
例子
@NotNull(message = "缺少必要的参数", groups ={IdGroup.class})
private Long id;
@NotBlank(message = "缺少物料编号", groups = {AddGroup.class})
private String productCode;
@NotBlank(message = "缺少物料编号", groups = {AddGroup.class})
private String productCode;
@NotEmpty(message = "缺少行项目信息")
@Valid
private List<PcoUnripeContractScopeEntity> details;
1. 验证groups包含IdGroup.class的参数
public interface IdGroup {
}
2. 验证groups包含AddGroup.class的参数 和不包含groups信息的参数
public interface AddGroup extends Default {
}
3. 验证groups包含AddGroup.class和IdGroup.class的参数 和不包含groups信息的参数 ,且当前一个验证组验证失败的情况下,不会继续验证后续验证组
@GroupSequence({AddGroup.class, IdGroup.class})
public interface Group {
}
(五) @Valid和和@Validated
-
@Valid注解用于校验,所属包为:javax.validation.Valid
-
@Validated是@Valid 的一次封装,是Spring提供的校验机制使用
-
@Validated提供分组验证功能
-
@Valid不提供分组验证功能,不支持分组的,只要groups存在费Default.class的 验证组,则不会验证该规则
-
@Valid可以使用在对象中的集合属性上,对该集合中对象数据进行校验
(六) 其他
1.快速失败模式
Spring Validation默认会校验完所有字段,然后才抛出异常。通过一些简单的配置,开启Fali Fast模式,一旦校验失败就立即返回
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast(true)
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
2.和代码生成器一起使用(volicity版)
原来
#foreach ($column in $columns)
@ApiModelProperty(value="$column.comments",name="$column.attrname",required=false)
private $column.attrType $column.attrname;
#end
#end
添加后
#foreach ($column in $columns)
#if($column.attrname=='id')
@NotNull(message = "缺少必要的参数",groups = {IdGroup.class})#end
#if($column.attrname !='id' && $column.attrType =='String' )
@NotBlank(message = "缺少$column.comments",groups = {AddGroup.class})#end
#if($column.attrname !='id' && $column.attrType !='String' )
@NotNull(message = "缺少$column.comments",groups = {AddGroup.class})#end
@ApiModelProperty(value="$column.comments",name="$column.attrname",required=false)
private $column.attrType $column.attrname;
#end
#end
3.警告
注意不要过度使用验证,只需要基础验证就行了,否则就会出现,写的时候爽,创建了各种groups, 改的时候难受的要死
作者:ZOUZDC
链接:https://juejin.cn/post/7028963866063306760
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。