validator最佳实践

本文探讨如何利用Spring框架中的javax.validation注解,通过HibernateValidator简化参数校验过程,实现代码的高效与简洁。同时,介绍了自定义校验标签及groups分组校验的实践方法。

validator的使用

在日常工作中,经常要对各种参数进行校验,代码中总是包含着各种if-else校验,导致代码相当冗长,而且稍不注意很容易踩坑。如何利用spring的javax.validation注解,高效写出简洁的代码,今天就是我们要讨论的话题。

为什么要用validator

  1. javax.validation注解可以帮我们完成很多参数校验,免去繁琐的串行校验
  2. 可用于各种项目程序中,不限于spring框架
  3. 基于JSR-303标准验证规范接口,方便扩展与升级

注解:

  1. JSR:Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
  2. JSR-303:是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

Bean Validation 中内置的 constraint
在这里插入图片描述

validator如何使用

由于Bean Validation只是提供基本的功能,在现实中,我们会遇到各种校验的需求,显然Bean Validation并不能很好符合我们的需要,因此本文将以Hibernate Validator作为实践案例做介绍,另外,由于我们只是讨论validator的使用,所以为了构建方便,我们使用spring-boot作为构建应用的基础框架。
注解:Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
在这里插入图片描述

Hibernate Validator注解

可查看org.hibernate.validator.constraints包路径下支持哪些注解

Hibernate Validator引入

由于使用spring-boot-starter-web,已经集合了hibernate-validator:6.0.13.Final和validation-api:2.0.1.Final,所以我们不需要阴引入,如果不是使用spring-boot,则需要自行引入。

 <!--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>6.0.13.Final</version>
 </dependency>

业务编写

  1. controller编写
@RequestMapping("/user/save")
   public BaseDto save(@Validated UserSaveRequest data) {
       return BaseDto.success();
   }
  1. bean的编写
public class BaseDto<T> {

  public BaseDto() {
  }

  public BaseDto(int code, String msg) {
      this.code = code;
      this.msg = msg;
  }

  private int code;
  private String msg;
  private T data;

  public int getCode() {
      return code;
  }

  public void setCode(int code) {
      this.code = code;
  }

  public String getMsg() {
      return msg;
  }

  public void setMsg(String msg) {
      this.msg = msg;
  }

  public T getData() {
      return data;
  }

  public void setData(T data) {
      this.data = data;
  }

  public static BaseDto success() {
      BaseDto data = new BaseDto();
      data.setCode(200);
      data.setMsg("OK");
      return data;
  }
  
  }
  public class UserSaveRequest {

  /**
   * 手机号
   */
  @NotBlank(message = "手机号不能为空")
  private String mobile;
}
  1. 验证异常代码统一处理
@RestControllerAdvice
public class GlobalExceptionController {
  
  @ExceptionHandler(BindException.class)
  public BaseDto handleMValidException(BindException e) {
      return BaseDto.fail(102, e.getBindingResult().getFieldError().getDefaultMessage());
  }
}
  1. 测试
{
	"code": 102,
	"msg": "联系邮箱不能为空"
}

自定义标签

添加手机号校验

  • 添加注解
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface PhoneNumber {
  String message() default "手机号不合法";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}

备注:

  • message 定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
  • groups 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
  • payload 主要是针对bean的,使用不多。
  1. 添加验证规则
 public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, Object> {

 public void initialize(PhoneNumber identityCardNumber) {
 }

 public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
     if (o == null) {
         return false;
     }
     String data = o.toString();
     Matcher m = Pattern.compile("^[1][3,4,5,6,7,8,9][0-9]{9}$").matcher(data);
     return m.find();
 }
}
  1. 对bean添加注解
 public class UserSaveRequest {
 @NotBlank(message = "手机号不能为空")
 @PhoneNumber(message = "手机号有误")
 private String mobile;
 }

使用groups的校验

场景:如果我们需要在插入的时候不校验手机号,但是在修改的时候要校验手机号,这时候就需要用上groups了

  1. 定义分组接口
public interface Create extends Default {
}
public interface Update extends Default {
}
  1. controller入参的改写
 @RequestMapping("/user/save")
 public BaseDto save(@Validated(Update.class) UserSaveRequest data) {
     return BaseDto.success();
 }

 @RequestMapping("/user/create")
 public BaseDto Create(@Validated(Create.class) UserSaveRequest data) {
     return BaseDto.success();
 }
  1. bean的改写
@NotBlank(message = "手机号不能为空", groups = Update.class)
 @PhoneNumber(message = "手机号有误", groups = Update.class)
 private String mobile;

最后,将会达到我们预期的结果

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值