SpringBoot + validator优雅参数校验,消除if-else

在我们日常开发中,参数校验必不可少,一般情况下我们习惯采用if-else来进行校验,结果就是满屏的if-else。代码既不优雅,也会令人眼花缭乱,维护也是繁琐。那么有没有优雅的参数校验呢?有,使用hibernate-validator吧,它会让你爱不释手。

废话不多说,撸起来吧。

1、简介

Hibernate Validate是Bean Validation实现的,内置了JSR303/JSR380中所有的constraint的实现,同时也额外提供了很多自定义的constraint。Bean Validation为JavaBean的验证提供了很多相关的元数据模型和API。

参数说明

在Hibernate Validate提供了很多注解,以实现对应参数的校验。

参数描述
@Null参数必须为null
@NotNull参数不能为null
@NotBlank参数不能为null和空值,一般作用在字符串类型
@AssertTrue参数必须为true
@AssertFalse参数必须为false
@Min(value)参数必须是数字,且大于等于指定的最小值
@Max(value)参数必须是数字,且小于等于指定的最大值
@DecimalMin(value)参数必须是数字,且大于等于指定的最小值
@DecimalMax(value)参数必须是数字,且小于等于指定的最大值
@Length(min,max)指定字符长度,最小值是多少,最大值是多少
@Size(max,min)参数的大小必须在指定的范围
@Digits(Integer,fraction)参数必须是个数字,且大小必须在可接受范围内
@Past参数必须是一个过去的日期
@Future参数必须是一个将来的日期
@Pattern(value)参数必须符合指定的正则表达式
@Email参数必须符合电子邮箱地址
@NotEmpty参数必须非空
@Range参数必须在合适的范围内

2、快速开始

在SpringBoot中使用Hibernate Validate特别简单,只需要引入jar包即可。

mavan引入:

 

xml

复制代码

<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.2.3.Final</version> </dependency>

gradle引入:

 

xml

复制代码

implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.2.3.Final'

封装统一异常处理类:

 

java

复制代码

/** * @author: jiangjs * @description: 统一异常处理方法 * @date: 2022/6/21 11:28 **/ @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler({ConstraintViolationException.class, MethodArgumentNotValidException.class}) public ResultUtil<String> resolveViolationException(Exception ex){ StringJoiner messages = new StringJoiner(","); if (ex instanceof ConstraintViolationException){ Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) ex).getConstraintViolations(); for (ConstraintViolation<?> violation : violations) { messages.add(violation.getMessage()); } } else { List<ObjectError> allErrors = ((MethodArgumentNotValidException) ex).getBindingResult().getAllErrors(); for (ObjectError error : allErrors) { messages.add(error.getDefaultMessage()); } } return ResultUtil.error(400,String.valueOf(messages)); } }

2.1 参数校验

2.1.1 创建实体
 

java

复制代码

/** * @author: jiangjs * @description: * @date: 2022/6/21 10:46 **/ @Data public class UserInfo { private Long userId; @NotBlank(message = "用户名不能为空") private String userName; @NotBlank(message = "密码不能为空") private String password; @Email(message = "请填写正确的email") @NotBlank(message = "email不能为空") private String email; }

上述实体中,我们看到使用了@NutBlank,@Email来校验String参数不能null和空,同时也校验了email是否符合email规则。

2.1.2 测试

使用Hibernate Validate则直接在参数前使用 @Validated注解即可。

 

less

复制代码

@PostMapping("/insertUserInfo.do") public ResultUtil<?> insertUserInfo(@Validated @RequestBody UserInfo userInfo){ return ResultUtil.success(userInfo.toString()); }

输出:

在参数校验时,会根据参数的顺序来进行校验,当一个参数不符合规则时,则直接返回校验结果。

  1. 我们在进行参数传递时,不填写username。

  1. 校验email是否符合规则

2.2 路径参数校验

我们在使用restful定义接口时,可能会使用@PathVariable来直接在接口url后传递参数,有时候传递的参数并不符合我们定义的参数规则,此时我们可以进行参数校验。

校验规则:参数:正则表达式

 

java

复制代码

@GetMapping("/getUserInfoByUserId.do/{userId:[0-9_]+}") public String getUserInfoByUserId(@PathVariable("userId") Integer userId){ return String.valueOf(userId); }

定义传递的userId必须是由数字组成。

输出:

当传递参数不符合正则时,则直接报错。

通过url进行参数校验,我们只看到了报错,并没有提示具体错误信息,也不能像使用注解那样自定义错误信息返回。

2.3 分组校验

在我们日常开发中常常涉及到更新,在更新的时候往往需要传递数据的Id,但是新增的时候又不需要这个Id,此时我们可以采用分组来进行数据Id的校验。

2.3.1 分组定义
 

java

复制代码

/** * @author: jiangjs * @description: * @date: 2022/6/21 10:46 **/ @Data public class UserInfo { public interface Update{}; @NotNull(message = "userId不能为空",groups = Update.class) private Long userId; @NotBlank(message = "用户名不能为空") private String userName; @NotBlank(message = "密码不能为空") private String password; @Email(message = "请填写正确的email") @NotBlank(message = "email不能为空") private String email; }

我们在实体类中定义了一个接口Update,然后在注解中的groups中指定即可。

2.3.2 测试
 

java

复制代码

/** * 分组校验数据信息:定义Update */ @PostMapping("/updateUserInfo.do") public ResultUtil<?> updateUserInfo(@Validated(value = UserInfo.Update.class) @RequestBody UserInfo userInfo){ return ResultUtil.success(userInfo.toString()); }

在@Validated中value指定实体中定义的接口。

输出:

2.4 校验传递

在日常开发中我们经常会在实体中引入其他实体,并且又要对其他实体中的数据进行数据校验,此时就存在检验传递,通常我们在需要校验的实体前使用 @Valid即可。

2.4.1 定义实体

添加一个雇员信息实体:

 

java

复制代码

/** * @author: jiangjs * @description: * @date: 2022/6/21 14:48 **/ @Data public class Employee { @NotBlank(message = "请填写雇员名称") private String name; }

在userInfo实体中引入Employee

 

java

复制代码

/** * @author: jiangjs * @description: * @date: 2022/6/21 10:46 **/ @Data public class UserInfo { public interface Update{}; @NotNull(message = "userId不能为空",groups = Update.class) private Long userId; @NotBlank(message = "用户名不能为空") private String userName; @NotBlank(message = "密码不能为空") private String password; @Email(message = "请填写正确的email") @NotBlank(message = "email不能为空") private String email; @Valid @NotNull(message = "雇员信息不能为空") private Employee employee; }

2.4.2 测试
 

java

复制代码

@PostMapping("/insertUserInfo.do") public ResultUtil<?> insertUserInfo(@Validated @RequestBody UserInfo userInfo){ return ResultUtil.success(userInfo.toString()); }

输出:

2.5 自定义校验

在参数校验时,可能Hibernate Validate自带的校验规则并不能满足我们业务需求,此时我们可以实现自定义校验规则。

需求:对身份证进行校验。

2.5.1 定义注解
 

java

复制代码

/** * @author: jiangjs * @description: 身份证校验规则 * @date: 2022/6/21 11:28 **/ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER,ElementType.FIELD}) @Constraint(validatedBy = IdCodeValidator.class) public @interface IdCode { String message() default "请填写正确的身份证信息"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }

@Constraint:Hibernate Validate提供的注解,用于实现自定义校验规则。其中validatedBy就是指定进行参数校验的实现类。

注:在自定义校验注解时必须带上groups()和payload(),否则会报错。

2.5.2 校验实现类
 

java

复制代码

/** * @author: jiangjs * @description: 身份证校验 * @date: 2022/6/21 14:25 **/ public class IdCodeValidator implements ConstraintValidator<IdCode,Object> { /** * 18位二代身份证号码的正则表达式 */ public static final String REGEX_ID_NO_18 = "^" // 6位地区码 + "\d{6}" // 年YYYY + "(18|19|([23]\d))\d{2}" // 月MM + "((0[1-9])|(10|11|12))" // 日DD + "(([0-2][1-9])|10|20|30|31)" // 3位顺序码 + "\d{3}" // 校验码 + "[0-9Xx]" + "$"; /** * 15位一代身份证号码的正则表达式 */ public static final String REGEX_ID_NO_15 = "^" + "\d{6}" // 6位地区码 + "\d{2}" // 年YYYY + "((0[1-9])|(10|11|12))" // 月MM + "(([0-2][1-9])|10|20|30|31)" // 日DD + "\d{3}"// 3位顺序码 + "$"; @Override public void initialize(IdCode constraintAnnotation) { ConstraintValidator.super.initialize(constraintAnnotation); } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { String idCode = String.valueOf(o); if (idCode.length() != 15 && idCode.length() != 18){ return false; } return idCode.matches(REGEX_ID_NO_18) || idCode.matches(REGEX_ID_NO_15); } }

ConstraintValidator:Hibernate Validate提供的接口,ConstraintValidator<A extends Annotation, T>,第一个参数必须是注解,即Annotation,第二个参数是泛型。同时在接口中提供了两个方法:

  • initialize(A constraintAnnotation),初始化注解,
  • boolean isValid(T var1, ConstraintValidatorContext var2):校验参数,true表示校验通过,false表示校验失败。
2.5.3 测试
 

java

复制代码

/** * 自定义校验数据信息 */ @PostMapping("/definedEmployeeInfo.do") public ResultUtil<?> definedEmployeeInfo(@Validated @RequestBody Employee employee){ return ResultUtil.success(employee.toString()); }

在Employee中添加校验注解:

 

java

复制代码

/** * @author: jiangjs * @description: * @date: 2022/6/21 14:48 **/ @Data public class Employee { @NotBlank(message = "请填写雇员名称") private String name; @IdCode(message = "请填写正确的身份证信息!") private String idCode; }

输出:

输入了错误的身份证,则校验不通过,提示定义的信息。

在日常工作中,使用Hibernate Validate进行参数校验真的会事半功倍,同时也提供了自定义校验的实现,让我们更加专注于业务。

好了,今天的分享就先到这,谢谢大家听我唠叨。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值