JSR303参数校验与commons-lang3的常见验证
一、JSR303参数校验
1.1、JSR303参数常用校验,使用的如下的包
在springboot项目中, validation的两个包已经包含在spring-boot-starter-web里面(可自己点进去看),无须再引包,如果不是springboot项目需要引入validation校验的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.5.RELEASE</version>
</dependency
validation的包
<!--jsr 303-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.1.Final</version>
</dependency>
使用方法:
①在实体类标注要限制的校验
例如:
@Pattern(regexp = "^[a-z0-9]+$", message = "user names can only be alphabetic and numeric")
@Length(max = 48, message = "user uuid length over 48 byte")
private String userUuid;
②在控制层参数处要标志@valid
例如:
@PostMapping
public ResponseVO createDataSet(@Valid @RequestBody DataSetSaveVO dataSetVO) {
return ResponseUtil.success(dataSetService.saveDataSet(dataSetVO));
}
1.2 JSR303 分组校验
1.2.1 写标志接口
这里写两个空接口(只要名字,接口无需内容,名字随意取,仅仅是个标志)
public interface UpdateGroup {
}
public interface AddGroup {
}
1.2.2 在实体类标记哪种情况执行哪种校验
@Data
public class UserEntity {
@Length(max = 5,message = "名字太长",groups = {UpdateGroup.class})
private String name;
@Max(value = 45)
private Integer age;
private Double salary;
@NotEmpty(groups = {UpdateGroup.class})
@Max(value = 4,message = "address最大长度不能超过4",groups = {AddGroup.class})
private String address;
private Long phoneNumber;
private String url;
}
这里以address字段为例,UpdateGroup标志校验@NotEmpty,而AddGroup标志校验@Max
1.2.3 在参数接收处指明要进行校验的类型
@PostMapping("/insertData")
@ApiOperation(value = "数据校验")
public R insertData(@Validated(value = UpdateGroup.class) @RequestBody UserEntity userEntity){
return R.ok();
}
①这里
@Validated(value = UpdateGroup.class)
表示只校验有UpdateGroup标记的字段,其他标记类型或者没标记group的皆不执行校验,相反,当参数接收处只有@Valid
没有标记分组,那么实体类只有没标记的注解才会生效,标记了分组的字段反而不生效(重点)
②这里是@Validated
与基础校验的@Valid
不一致
③实体字段的group中可以标记多个接口类,即满足任一条件即可执行:例如
@Max(value = 4,message = "address最大长度不能超过4",groups = {AddGroup.class,UpdateGroup.class})
private String address;
1.3 JSR303 自定义校验
有时候JSR303自带的校验方法不满足我们的校验规则,那么我们就自定义一个校验规则:
例如:我们想让某字段从 指定数据取值,例如这里的1,2
@ListValue(vals = {1,2},message = "请选择指定值")
private int phoneNumber;
1.3.1 写一个注解
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
String message() default "{value not correct}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default{};
}
注意:①@Constraint(validatedBy = {ListValueConstraintValidator.class})
代表本注解使用哪个校验器来校验参数,可以写多个校验器,用逗号隔开
②String message() default "{value not correct}";
是注解不写message时,默认的提示信息
③ Class<?>[] groups() default { };
表示可以指定分组(参考1.2)
④ int[] vals() default{};
就是注解上的指定值
1.3.2 写校验器
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化方法vals 是获取注解指定的可选值,例如vals = {1,2}
* @param constraintAnnotation
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
if(vals != null && vals.length>0){
for(int val:vals){
set.add(val);
}
}
}
/**
* 校验参数是否满足条件
* @param value 需要校验的值(即用户提交的值)
* @param context
* @return 校验满足条件返回true,否则返回false
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
1.3.3 关联注解和校验器
@Constraint(validatedBy = {ListValueConstraintValidator.class})
就是指定本注解使用的校验器,可以使用多个校验器
1.4 异常捕获
参数校验失败的异常是来自controller,来写一个全局异常捕获器
@Slf4j
@RestControllerAdvice(basePackages = "com.sinux.inhaite.idempotent.jsr303")
public class DesignExceptionControllerAdvice {
/**
* 捕获MethodArgumentNotValidException型异常
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e) {
log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());
BindingResult bindingResult =e.getBindingResult();
Map<String,String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach(item -> {
errorMap.put(item.getField(),item.getDefaultMessage());
});
return R.error(400,"数据校验失败").put("data",errorMap);
}
/**
* 通用异常捕获
* @param throwable
* @return
*/
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
log.error("错误:",throwable);
return R.error(400,"系统异常:"+throwable.getMessage());
}
}
说明:@RestControllerAdvice(basePackages = "com.sinux.inhaite.idempotent.jsr303")
是指定本异常捕获类对哪个包生效
1.5 代码中间的校验
有时候对象的转换在service层中的代码,没法像controller层加@Valid注解来校验,在代码中间校验JSR303的办法:
①写一个校验器类
import commonutils.pingan.AssertUtil;
import commonutils.pingan.enums.ComErrorCode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.text.MessageFormat;
import java.util.Set;
/**
* 参数校验工具类
*/
@Component
public class ValidatorUtil {
//校验器
private static Validator validator;
/**
* 参数校验,如果校验失败则抛出异常
* @param obj
*/
public static void validate(Object obj){
//执行检验
Set<ConstraintViolation<Object>> validate = validator.validate(obj);
//检验信息为空则表示检验通过,反之不通过并抛出异常
validate.stream().forEach(constraint ->{
boolean result = StringUtils.isBlank(constraint.getMessage());
AssertUtil.assertTrue(result,
ComErrorCode.PARAM_VALID_ERROR.getCode(),
MessageFormat.format(ComErrorCode.PARAM_VALID_ERROR.getMessage(),constraint.getPropertyPath().toString(),constraint.getMessage()));
});
}
@Autowired
public void setValidator(Validator validator){
ValidatorUtil.validator=validator;
}
}
②代码中调用
ValidatorUtil.validate(要校验的对象);
这样就能在代码中间使用JSR303校验数据
1.6 @Valid和@Validated的区别
二、commons-lang3参数校验
2.1 引入包
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
校验方法汇总: