• 浅析JSR303数据校验


    一、后端校验技术

      JSR303技术,JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint,在springboot中使用也比较简便。

    1、给需要校验的字段添加校验注解

      在javax.validation.constraints包下有许多的注解:

      Hibernate Validator 附加的 constraint

      常用的校验注解补充:

    @NotBlank 检查约束字符串是不是Null,还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格。

    @NotEmpty 检查约束元素是否为NULL或者是EMPTY

    @Length 被检查的字符串长度是否在指定的范围内

    @CreditCardNumber信用卡验证

    @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。

    @URL 验证是否是一个url地址

      注意:一个字段可以标注多个校验注解。

    2、给需要检验的方法标准@Valid

    如果只标注了注解字段,不启用@valid的是不生效的。

    3、捕捉校验异常,返回提示信息

      对于程序可能有很多的校验注解,可能会出现多个校验错误,我们可以定义一个统一的异常处理类,帮我们捕捉校验错误并返回提示信息,这里可以利用Spring的ControllerAdvice技术。

    (1)编写统一异常处理类

    package io.renren.app.exception;
    ​
    import io.renren.common.utils.R;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    ​
    import java.util.HashMap;
    import java.util.Map;
    ​
    @RestControllerAdvice(basePackages = "io.renren.app.controller")
    @Slf4j
    public class AppManageControllerAdvices {
    ​
     @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(400, "数据校验失败").put("data", errorMap);
     }
    ​
     @ExceptionHandler(value = Throwable.class)
     public R handleException(Throwable throwable) {
    ​
     log.error("错误:", throwable);
     return R.error(500, "系统未知异常");
     }
    }

    (2)出现异常将异常抛出

      对于校验可能会出现的异常,我们将其抛出,不予捕捉感知,都交给我们的统一异常处理类处理,返回提示信息。

    4、分组校验(多场景的复杂校验)

      对于不同的操作,字段校验的规则和数量可能是不同的,所以我们将校验规则分组,对于不同的操作进行不同的校验组,使用groups属性。

    (1)想要使用分组校验功能,根据文档我们首先编写不同的校验组接口,只编写空接口,用来表示就可以了:

    //新增分组
    public interface AddGroup{
    ​
    }
    //修改分组
    public interface UpdateGroup{
    ​
    }

    (2)编写好分组接口,对于不同的检验规则,标注不同的分组标识

    @NotNull ( message ="修改必须指定id",groups = {UpdateGroup.class})
    @Null(message = "新增不能指定id",groups = {AddGroup.class})
    @TableId
    private Long Id;
    ​
    @NotBlank(message="名称必须提交",groups={AddGroup.class,UpdateGroup.class})
    private String name;

    (3)在controller方法上标注不同的分组校验,使用@Validated注解

     //保存
     @RequestMapping("/save")
     //@RequiresPermissions("product:brand:save")
     public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
     brandService.save(brand);
     return R.ok();
     }

      @Validated({AddGroup.class}):启用不同的分组校验规则。

      注意:在使用分组校验的情况下,对于没有标注分组的校验规则,默认是不生效的,只有标注了分组的校验规则才会生效。

    二、自定义校验

    1、编写一个自定义的校验注解

      导入依赖:
    <dependency>
     <groupId>javax.validation</groupId>
     <artifactId>validation-api</artifactId>
     <version>2.0.1.Final</version>
    </dependency>

      编写自定义校验注解

    package com.zunhui.common.valid;
    ​
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    ​
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.ElementType.TYPE_USE;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    ​
    @Documented
    //指定校验器
    @Constraint(validatedBy = {com.zunhui.common.valid.ListValueConstraintValidator.class })
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    public @interface ListValue {
     //配置默认的提示消息,新建配置文件ValidationMessages.properties在其中配置:
     //com.zunhui.common.valid.ListValue.message = 必须提交指定的值
     String message() default="{com.zunhui.common.valid.ListValue.message}";
    
     Class<?>[] groups() default { };
    ​
     Class<? extends Payload>[] payload() default { };
    ​
     int[] vals() default { };
    }

      用来验证自定义的字段值,非0即1。

    2、编写一个自定义的校验器
    package com.zunhui.common.valid;
    ​
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.util.HashSet;
    import java.util.Set;
    ​
    public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    ​
     private Set<Integer> set = new HashSet<Integer>();
     //初始化方法
     public void initialize(ListValue constraintAnnotation) {
     int[] vals = constraintAnnotation.vals();
     for (int val : vals) {
     set.add(val);
     }
     }
     //判断是否校验成功
     /**
     * @param value 需要校验的值
     * @param context
     * @return
     */
     public boolean isValid(Integer value, ConstraintValidatorContext context) {
     return set.contains(value);
     }
    }

    3、关联自定义的校验器和自定义的校验注解

    @Constraint(validatedBy = {com.zunhui.common.valid.ListValueConstraintValidator.class })

      在自定义的校验注解中添加自己的校验器,就关联好了,一个校验注解可以指定多个不同类型的校验器,适配不同类型的校验。关联完成就可以使用了。

    4、使用
    //校验指定字段非0即1
    @ListValue(valus = {0,1},message="提示消息,不指定则默认读取配置文件中的")
    private Integer showStatus;
    作者:稚友22
    链接:https://www.jianshu.com/p/d2ddd856cce2
  • 相关阅读:
    吴裕雄--天生自然 诗经:离思五首·其四
    吴裕雄--天生自然 诗经:江城子·乙卯正月二十日夜记梦
    CentOS6—启动httpd失败—Certificate has expired
    paper—SCI—Examples of author responses to reviewer comments
    paper—SCI答复审稿人的回信技巧
    mysql android—Installation using AndroPHP
    linux socket c : send data when socket close—SIGPIPE, Broken pipe
    Firefox:曾经打破黑暗的产品
    mysql中datetime到time_t转换
    mysql datetime 时间比较
  • 原文地址:https://www.cnblogs.com/goloving/p/14837990.html
Copyright © 2020-2023  润新知