JSR303校验(分组校验,自定义校验)

JSR303校验(分组校验,自定义校验)

JSR303的使用:

使用步骤:

1.添加依赖

2.在entity类的属性上添加注解

3.开启校验功能:在controller类的方法的参数上加上@Valid属性

4.校验失败的处理。

@Data
public class EmployeeReq {


    @NotEmpty(message = "请选择员工类型")
    private String employeesType;

    @NotEmpty(message = "请输入员工姓名")
    private String name;

    @NotEmpty(message = "请输入IC卡号")
    private String icNo;

    @NotEmpty(message = "请输入联系电话")
    private String mobilePhone;

}

controller层 方法参数中加@Valid注解

@RestController
 @RequestMapping("/web/employee")
public class EmployeeController {
 
    @Autowired
    private EmployeeService employeeService;


    @PostMapping("/addEmployee")
   public void createEmployee(@Valid @RequestBody EmployeeReq employeeReq, BindingResult bindingResult) throws Exception {
        Util.checkBindingResult(bindingResult);
         employeeService.createEmployee(employeeReq);
    }

 }
工具类:1.校验失败处理
 public class Util {

   public static void checkBindingResult(BindingResult bindingResult) throws Exception {
        if (bindingResult == null) {
            return;
     }
       if (bindingResult.hasErrors()) {
         String errorMessage = bindingResult.getAllErrors().get(0).getDefaultMessage();
         if (errorMessage != null && errorMessage.length() > 50) {
              errorMessage = "参数错误";
          }
            throw new ComacException(errorMessage);
       }
    }
 }
定义处理校验失败异常类:2.全局处理

1.定义处理全局异常类

//集中处理异常
@Slf4j
//@RestControllerAdvice(basePackages = "cn.jinronga.gulimall.product.controller")
//@ResponseBody
@RestControllerAdvice(basePackages = "cn.jinronga.gulimall.product.controller")
public class GuilimallExceptionControllerAdvice {


    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();

        Map<String,String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){

        log.error("错误:",throwable);
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

1.定义异常枚举类:

/***
 * 错误码和错误信息定义类
 * 1. 错误码定义规则为5为数字
 * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
 * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
 * 错误码列表:
 *  10: 通用
 *      001:参数格式校验
 *  11: 商品
 *  12: 订单
 *  13: 购物车
 *  14: 物流
 *
 *
 */
public enum BizCodeEnume {
    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VAILD_EXCEPTION(10001,"参数格式校验失败");

    private int code;
    private String msg;
    BizCodeEnume(int code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}
JSR303定义的校验类型
空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.

Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true
@AssertFalse    验证 Boolean 对象是否为 false

长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) Validates that the annotated string is between min and max included.

日期检查
@Past           验证 Date 和 Calendar 对象是否在当前时间之前
@Future     验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern    验证 String 对象是否符合正则表达式的规则

数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min            验证 Number 和 String 对象是否大等于指定的值
@Max            验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits     验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。

@Range(min=, max=) 检查数字是否介于min和max之间.
@Range(min=10000,max=50000,message="range.bean.wage")
private BigDecimal wage;

@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email  验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)

分组校验:

步骤:

  1. 在校验注解上加上groups = {xxx.class, ...}属性,值可以是任意interface接口,例如

    @URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class})

  2. 在开启校验处,将@Valid注解改为@Validated({xxx.class}),例如@Validated({AddGroup.class})就表示只校验该组的属性;

    注意:未添加任何分组的校验将会无效,开启娇艳的时候i如果添加了分组信息,那么只会校验同样页添加了该分组的属性。

案例:

定义两个空接口作为标记:

public interface AddGroup {
}

public interface UpdateGroup {
}

实体类:groups = {xxx.class}分组校验

@Data
@TableName("pms_brand")
@Validated  //数据校验
public class BrandEntity implements Serializable {
   private static final long serialVersionUID = 1L;
   /**
    * 品牌id
    */
   @NotNull(message = "修改必须制定品牌id",groups = {UpdateGroup.class})
   @Null(message = "新增不能制定品牌id",groups = {AddGroup.class})
   @TableId
   private Long brandId;
   /**
    * 品牌名
    */
   @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
   private String name;
   /**
    * 品牌logo地址
    */
   @NotEmpty(groups = {AddGroup.class})
   @URL(message = "logo必须是一个合法的url地址",groups = {AddGroup.class,UpdateGroup.class})
   private String logo;
   /**
    * 介绍
    */
   private String descript;
   /**
    * 显示状态[0-不显示;1-显示]
    */
   private Integer showStatus;
   /**
    * 检索首字母
    */
   @NotEmpty(groups = {AddGroup.class})
   @Pattern(regexp = "[a-zA-Z]$",message = "检索首字母必须是一个字母",groups = {AddGroup.class,UpdateGroup.class} )
   private String firstLetter;
   /**
    * 排序
    */
    @NotNull(groups = {AddGroup.class})
     @Min(value = 0,message = "排序必须大于0",groups = {AddGroup.class,UpdateGroup.class})
   private Integer sort;

}

@Valid注解改为@Validated({xxxx.class})

@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand/*, BindingResult bindingResult*/){
    brandService.save(brand);

    return R.ok();
}

注:

分组校验(多场景复杂校验)

  1. @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
    给校验注解标注什么情况需要进行校验

  2. @Validated({AddGroup.class})

 **统一处理异常**

 默认没有指定分组校验注解@NotBlank,在分组校验情况下不生效的,只会在@Validated下生效

自定义校验

JSR303 是 Java EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是hibernate Validator,有了它,我们可以在实体类的字段上标注不同的注解实现对数据的校验,不用 if-else 判断,简化了我们的开发,而且可读性也很好。

给字段 showStatus上标注自定义注解 @ListValue

	/**
	 * 显示状态[0-不显示;1-显示]
	 */
    @ListValue(vals={0,1},groups = AddGroup.class)
	private Integer showStatus;

1、编写一个自定义的校验注解创建注解 @ListValue,可以参考官方的注解,比如 @NotNull,我们只需要修改下面注释的几处即可

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
    //配置文件中错误提示信息的名称
    String message() default "{cn.jinronga.gulimall.product.valid.ListValue}";

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

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

    //自定义值的类型
    int[] vals()default{};
}

2.创建自定义约束校验器,继承 ConstraintValidator,第一个泛型是自定义注解、第二个是校验值的类型,也即注解标注的字段的类型

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {

    private Set<Integer> set=new HashSet<>();
   //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals=constraintAnnotation.vals();
        for (int val:vals){
            set.add(val);
        }
    }

    //判断是否判断成功

    /**
     *
     * @param value 需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}

3.在 resources 目录下创建一个 ValidationMessages.properties 配置文件,key 是第二步 message 设置的默认值,value 是自定义错误信息

cn.jinronga.gulimall.product.valid.ListValue="必须自定的值"

自定义注解 @ListValue 就可以工作了,当然这只是很简单的校验,但方法大同小异。

原文地址:https://www.cnblogs.com/jinronga/p/13299890.html