美文网首页
关于参数校验,hibernate的validator 的校验

关于参数校验,hibernate的validator 的校验

作者: dancer4code | 来源:发表于2021-08-31 17:38 被阅读0次

1.注解式校验

1.1 常见校验注解

定义的校验类型

@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@URL(protocol=,host=, port=,regexp=, flags=) ip地址校验

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=) Checks whether the annotated value lies between (inclusive) the specified minimum and maximum.

1.2 实现简单的注解式校验栗子

1.2.1 请求实体

@Data
public class CommonReqVO<T> {
    /**
     * 互感器主键
     */
    @NotNull(message = "业务类型不能为空")
    private String type;
    /**
     * 请求数据
     */
    @Valid
    @NotNull(message = "数据不能为空")
    private T data;

    @NotNull(message = "开始时间不能为空!")
    @Pattern(regexp = "^((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[4]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))\\s+([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$",message = "日期格式不正确")
//    @DateTime(format = "yyyy-MM-dd", message = "时间格式错误,格式应为'yyyy-MM-dd'")
    private String startDate;
}

@Data
public class UserVO {

    @NotBlank(message = "name不能为空",groups = S1.class)
    private String name;
    @Range(min = 1,max = 120,message = "年龄必须在1-120之间",groups = S1.class)
    private Integer age;
    @NotBlank(message = "address不能为空",groups = S2.class)
    private String address;
    @NotBlank(message = "phone不能为空",groups = S2.class)
    private String phone;


    public interface S1{}
    public interface S2{}
}

1.2.2 controller

@RestController
@RequestMapping("test")
public class TestController {
    
    @PostMapping("t3")
    public String test3(@RequestBody CommonReqVO<UserVO> userVO){
        ValidateUtil.validateParams(userVO,"请求参数不能为空");
        if ("01".equals(userVO.getType())){
            ValidateUtil.validateParams(userVO,"请求参数不能为空",UserVO.S1.class);
        }else if("02".equals(userVO.getType())){
            ValidateUtil.validateParams(userVO,"请求参数不能为空",UserVO.S2.class);
        }else {
            throw new CustomException("请求参数type类型不匹配!");
        }
        return "t3";
    }

    @PostMapping("t4")
    public String test4(@RequestBody @Validated CommonReqVO<UserVO> userVO){
        return "4";
    }
}

1.2.3 统一异常处理类及相关类

@RestControllerAdvice
@Slf4j
public class ExceptionHandle {

    @Value("${validator.showField:false}")
    private Boolean showFile;

    @ExceptionHandler(CustomException.class)
    public ResultInfo handleCustomException(CustomException e){
        log.error("【用户自定义异常】{}", e);
        return ResultInfoUtil.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResultInfo handleReqException(HttpMediaTypeNotSupportedException e){
        log.error("【请求类型不支持异常】{}", e);
        if (e.getMessage().contains("not supported")){
            return ResultInfoUtil.error(0, "请求Content-Type不匹配");
        }
        return ResultInfoUtil.error(0, e.getMessage());
    }
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultInfo handleReqBodyException(HttpMessageNotReadableException e){
        log.error("【请求体异常】{}", e);
        if (e.getMessage().contains("Required request body is missing")){
            return ResultInfoUtil.error(0, "请求体不能为空");
        }
        return ResultInfoUtil.error(0, e.getMessage());
    }


    @ExceptionHandler(Exception.class)
    public ResultInfo handleOtherException(Exception e){
        log.error("【系统异常】{}", e);
        return ResultInfoUtil.error(0, e.getMessage());
    }


    /**
     * 处理validation框架中的{@link MethodArgumentNotValidException}异常
     * <p>该异常一般出在用{@code @RequestBody}注解标记的参数校验,在对象中的参数有问题是抛出</p>
     *
     * @param e {@link MethodArgumentNotValidException}
     * @return {@link ResultInfo}
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultInfo doNoValidExceptionHandler(MethodArgumentNotValidException e) {
        log.info("捕捉到参数校验异常: [{}]", e);
        return validationExceptionHandler(e.getBindingResult(), e.getMessage());
    }

    /**
     * 处理validation框架中的{@link ConstraintDeclarationException}异常
     * <p>该异常一般出在用{@code @RequestParam}注解标记的参数校验</p>
     *
     * @param e {@link ConstraintDeclarationException}
     * @return {@link ResultInfo}
     */
    @ExceptionHandler(ConstraintDeclarationException.class)
    public ResultInfo doNoValidExceptionHandler(ConstraintDeclarationException e) {
        String errorMsg = e.getMessage();
        log.info("捕捉到参数校验异常: [{}]", errorMsg, e);
        return ResultInfoUtil.error(0, e.getMessage());
    }

    /**
     * 处理validation框架中的{@link BindException}异常
     * <p>该异常一般出在用{@code @RequestBody}注解标记的参数校验,在对象嵌套类型参数有问题时抛出</p>
     *
     * @param e {@link BindException}
     * @return {@link ResultInfo}
     */
    @ExceptionHandler(BindException.class)
    public ResultInfo doNoValidExceptionHandler(BindException e) {
        log.info("捕捉到参数校验异常: [{}]", e);
        return validationExceptionHandler(e.getBindingResult(), e.getMessage());
    }


    /**
     * 处理validation框架的异常
     *
     * @param bindingResult {@link BindingResult} 异常绑定的对象信息
     * @param message       异常信息
     * @return {@link ResultInfo}
     */
    private ResultInfo validationExceptionHandler(BindingResult bindingResult, String message) {
        //默认捕获第一个不符合校验规则的错误信息
        //错误字段对象
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        List<String> errorMessages = new ArrayList<>(fieldErrors.size());
        if (showFile){
            fieldErrors.stream().forEach(e->{
                errorMessages.add(e.getField()+"-"+e.getDefaultMessage());
            });
        }else {
            fieldErrors.stream().forEach(e->{
                errorMessages.add(e.getDefaultMessage());
            });
        }
        log.info("捕捉到参数校验异常详情: {}", errorMessages);
        return ResultInfoUtil.error(0, errorMessages.toString());
    }

}

@Data
public class ResultInfo<T> {
    /**
     *返回码
     */
    private Integer code;
    /**
     *返回提示信息
     */
    private String msg;
    /**
     *返回具体对象
     */
    private T data;
}

public class ResultInfoUtil {
    public static ResultInfo success(Object object) {
        ResultInfo result = new ResultInfo();
        ResultInfoEnum successEnum = ResultInfoEnum.SUCCESS;
        result.setCode(successEnum.getCode());
        result.setMsg(successEnum.getMsg());
        result.setData(object);
        return result;
    }

    public static ResultInfo success() {
        return success(null);
    }

    public static ResultInfo error(Integer code, String msg) {
        ResultInfo result = new ResultInfo();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}

@Data
public class CustomException extends RuntimeException {

    private Integer code;

    public CustomException(ResultInfoEnum resultInfoEnum) {
        super(resultInfoEnum.getMsg());
        this.code = resultInfoEnum.getCode();
    }

    public CustomException(String msg) {
        super(msg);
        this.code = ResultInfoEnum.FAILED.getCode();
    }

}

注意上面配置了 failFast 为false。所以会校验全部参数

1.2.4 请求及响应

请求

{}

响应

{
    "code": 0,
    "msg": "[data-数据不能为空, type-业务类型不能为空, startDate-开始时间不能为空!]",
    "data": null
}

2.编程式校验

2.1 校验相关类

@Configuration
public class ValidateUtil<T> {


    private static Validator validator;
    /**
     * 是否启用快速失败
     */
    private static Boolean failFast;
    /**
     * 是否显示错误字段
     */
    private static Boolean showField;

    public static void setFailFast(Boolean failFast) {
        ValidateUtil.failFast = failFast;
    }


    public static void setShowField(Boolean showField) {
        ValidateUtil.showField = showField;
    }

    /**
     * @param obj 校验的对象
     * @param <T>
     * @return
     */
    public static <T> Set<ConstraintViolation<T>> validate(T obj, Class... groupClasses) {
        Set<ConstraintViolation<T>> constraintViolations;
        if (groupClasses != null && groupClasses.length > 0) {
            constraintViolations = validator.validate(obj, groupClasses);
        } else {
            constraintViolations = validator.validate(obj);
        }
        return constraintViolations;
    }

    /**
     * 验证请求参数是否为空
     *
     * @param o   参数对象
     * @param msg 报错信息
     */
    public static void checkIsNotNull(Object o, String msg) {
        if (o == null) {
            throw new CustomException(msg);
        }
    }

    public static <T> void validateParams(T t, String msg, Class... groupClasses) {
        checkIsNotNull(t, msg);
        Set<ConstraintViolation<T>> constraintViolationSet = validate(t, groupClasses);
        List<String> resultString = new ArrayList<>(constraintViolationSet.size());
        if (!constraintViolationSet.isEmpty()) {
            if (showField){
                constraintViolationSet.stream().forEach(c -> {
                    resultString.add(c.getPropertyPath().toString() + "-" + c.getMessage());
                });
            }else {
                constraintViolationSet.stream().forEach(c -> {
                    resultString.add(c.getMessage());
                });
            }
            throw new CustomException(resultString.toString());
        }
    }

    /**
     * 注入validator
     *
     * @param validatorBean
     */
    public static void setValidator(Validator validatorBean) {
        validator = validatorBean;
    }


}
@Configuration
public class ValidatorConfiguration {

    @Value("${validator.failFast:true}")
    private Boolean failFast;

    @Value("${validator.showField:false}")
    private Boolean showField;

    @Autowired
    private ServletContext servletContext;

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败
                .failFast(failFast)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }

    /**
     * 给ValidateUtil初始化
     */
    @PostConstruct
    public void initValidateUtil() {
        ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
        Validator validator = (Validator) applicationContext.getBean("validator");
        ValidateUtil.setValidator(validator);
        ValidateUtil.setFailFast(failFast);
        ValidateUtil.setShowField(showField);
    }
}

2.2 配置

#校验相关配置
validator:
  #快速失败
  failFast: true
  #错误提示是否显示错误字段
  showField: false

3.自定义校验注解

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateTime.DateTimeValidator.class)
public @interface DateTime {

    String message() default "格式错误";

    String format() default "yyyyMM";

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

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


    class DateTimeValidator implements ConstraintValidator<DateTime, String> {

        private DateTime dateTime;

        @Override
        public void initialize(DateTime dateTime) {
            this.dateTime = dateTime;
        }

        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            // 如果 value 为空则不进行格式验证,为空验证可以使用 @NotBlank @NotNull @NotEmpty 等注解来进行控制,职责分离
            if (value == null) {
                return true;
            }
            String format = dateTime.format();
            if (value.length() != format.length()) {
                return false;
            }
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
            try {
                simpleDateFormat.parse(value);
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    }
}

3.1 使用方法

@DateTime(format = "yyyy-MM-dd", message = "时间格式错误,格式应为'yyyy-MM-dd'")
private String startDate;

相关文章

网友评论

      本文标题:关于参数校验,hibernate的validator 的校验

      本文链接:https://www.haomeiwen.com/subject/xewziltx.html