• 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 就可以工作了,当然这只是很简单的校验,但方法大同小异。

  • 相关阅读:
    抽象工厂学习笔记
    SQL Transcation的一些总结
    享元模式(Flyweight)
    编程语言简史
    代理模式(Proxy)
    打造属于你的提供者(Provider = Strategy + Factory Method)
    打造属于你的加密Helper类
    单例模式(Singleton)的6种实现
    在C#中应用哈希表(Hashtable)
    InstallShield脚本语言的编写
  • 原文地址:https://www.cnblogs.com/jinronga/p/13299890.html
Copyright © 2020-2023  润新知