zoukankan      html  css  js  c++  java
  • Springboot参数校验最全使用

    引入maven依赖(可选)

    如果我们的项目使用了Spring Boot,hibernate validator框架已经集成在 spring-boot-starter-web中,所以无需再添加其他依赖。如果不是Spring Boot项目,则需要添加如下依赖:

    1 <dependency>
    2     <groupId>org.hibernate.validator</groupId>
    3     <artifactId>hibernate-validator</artifactId>
    4     <version>6.0.8.Final</version>
    5 </dependency>

    常用注解介绍

    注解 作用类型 来源 说明
    @Null 任何类型   属性必须为null
    @NotNull 任何类型   属性不能为null
    @NotEmpty 集合 hibernate validator扩展注解 集合不能为null,且size大于0
    @NotBlank 字符串、字符 hibernate validator扩展注解 字符类不能为null,且去掉空格之后长度大于0
    @AssertTrue Boolean、boolean   布尔属性必须是true
    @Min 数字类型(原子和包装)   限定数字的最小值(整型)
    @Max 同@Min   限定数字的最大值(整型)
    @DecimalMin 同@Min   限定数字的最小值(字符串,可以是小数)
    @DecimalMax 同@Min   限定数字的最大值(字符串,可以是小数)
    @Range 数字类型(原子和包装) hibernate validator扩展注解 限定数字范围(长整型)
    @Length(min=,max=) 字符串 hibernate validator扩展注解 限定字符串长度
    @Size 集合   限定集合大小
    @Past 时间、日期   必须是一个过去的时间或日期
    @Future 时期、时间   必须是一个未来的时间或日期
    @Email 字符串 hibernate validator扩展注解 必须是一个邮箱格式
    @Pattern 字符串、字符   正则匹配字符串

    单参数校验

    controller类上必须添加@Validated注解,如下所示:

    1 @RestController
    2 @RequestMapping("/user")
    3 @Validated // 需要添加的注解
    4 public class UserController {
    5   // do something
    6 }

    方法参数前面加上注解即可, 如下所示:

    1 public Result deleteUser(@NotNull(message = "id不能为空") Long id) {
    2   // do something
    3 }

    对象参数校验

    对象参数校验使用时,需要先在对象的校验属性上添加注解,然后在Controller方法的对象参数前添加@Validated注解,如下所示:

     1 public Result addUser(@RequestBody @Validated UserDto userDto) {
     2     // do something
     3 }
     4 
     5 public class UserDto {
     6   @NotBlank(message="名称不能为空")
     7   private String name;
     8 
     9   @NotNull(message="年龄不能为空")
    10   private Integer age;
    11   
    12   ……
    13 }

    嵌套对象校验

    如果需要校验的参数对象中还嵌套有一个对象属性,而该嵌套的对象属性也需要校验,那么就需要在该对象属性上增加@Valid注解。

     1 public class UserDto {
     2     @NotNull
     3     private Long id;
     4     
     5     @NotBlank
     6     private String name;
     7 
     8     @NotNull
     9     private Integer age;
    10     
    11     @Valid
    12     private Phone phone;
    13     
    14     ……
    15 }
    16 
    17 public class Phone {
    18     @NotBlank
    19     private String type;
    20     
    21     @NotBlank
    22     private String phoneNum;
    23 }

    分组校验

    在对象参数校验场景下,有一种特殊场景,同一个参数对象在不同的场景下有不同的校验规则。比如,在创建对象时不需要传入id字段(id字段是主键,由系统生成,不由用户指定),但是在修改对象时就必须要传入id字段。在这样的场景下就需要对注解进行分组。

    1)组件默认有个分组Default.class, 所以我们可以再创建一个分组例如UpdateAction.class,如下所示:

    1 public interface UpdateAction {
    2 }

    2)在参数类中需要校验的属性上,在注解中添加groups属性(表示Default.class默认情况下会校验name,age参数,而在UpdateAction.class情况下会校验id参数),如下所示:

     1 public class UserDto {
     2     @NotNull(groups = {UpdateAction.class}, message = "id不能为空")
     3     private Long id;
     4     
     5     @NotBlank
     6     private String name;
     7 
     8     @NotNull
     9     private Integer age;
    10     
    11     ……
    12 }

     3)在controller的方法中,在@Validated注解里指定哪种场景即可(没有指定就代表采用Default.class,采用其他分组则需要显示指定)。如下代码便表示在addUser()接口中按照默认情况进行参数校验,在updateUser()接口中按照默认情况和UpdateAction分组对参数进行共同校验。

    1 public Result addUser(@Validated UserDto userDto) {
    2   // do something
    3 }
    4 
    5 public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserDto userDto) {
    6   // do something
    7 }

    枚举校验

    枚举校验hibernate validator并不支持。需要自己扩展实现。

    1) 定义EnumChck注解,如下所示:

     1 @Documented
     2 @Constraint(validatedBy = EnumConstraintValidator.class)
     3 @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
     4 @Retention(RUNTIME)
     5 @Repeatable(EnumCheck.List.class)
     6 public @interface EnumCheck {
     7     String message() default "{javax.validation.constraints.EnumCheck.message}";
     8 
     9     Class<?>[] groups() default { };
    10 
    11     Class<? extends Payload>[] payload() default { };
    12 
    13     /**
    14      * 枚举类
    15      *
    16      */
    17     Class<? extends EnumValidator> clazz();
    18 
    19     /**
    20      * 调用的方法名称
    21      */
    22     String method() default "getValue";
    23 
    24     @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    25     @Retention(RUNTIME)
    26     @Documented
    27     @interface List {
    28         EnumCheck[] value();
    29     }
    30 }

    2) 定义EnumValidator接口,让需要校验的枚举类实现其接口的getValue()方法,如下所示:

    1 public interface EnumValidator {
    2     Object getValue();
    3 }

    3)自定义实现EnumConstraintValidator,需要实现ConstraintValidator接口,如下所示:

     1 public class EnumConstraintValidator implements ConstraintValidator<EnumCheck, Object> {
     2     /**
     3      * 注解对象
     4      */
     5     private EnumCheck annotation;
     6 
     7     /**
     8      * 初始化方法
     9      *
    10      * @param constraintAnnotation 注解对象
    11      */
    12     @Override
    13     public void initialize(EnumCheck constraintAnnotation) {
    14         this.annotation = constraintAnnotation;
    15     }
    16 
    17     @Override
    18     public boolean isValid(Object value, ConstraintValidatorContext context) {
    19         if (Objects.isNull(value)) {
    20             return false;
    21         }
    22 
    23         Object[] enumConstants = annotation.clazz().getEnumConstants();
    24         try {
    25             Method method = annotation.clazz().getMethod(annotation.method());
    26             for (Object enumConstant : enumConstants) {
    27                 if (value.equals(method.invoke(enumConstant))) {
    28                     return true;
    29                 }
    30             }
    31         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    32             throw new RuntimeException(e);
    33         }
    34 
    35         return false;
    36     }
    37 }

    4) 具体使用步骤如下:

    具体枚举类实现上面定义的EnumValidator接口:

     1 public enum RefNodeType implements EnumValidator {
     2     PROJECT("project", "项目"),
     3     FIELD("field", "变量"),
     4     FUNC("func", "函数");
     5 
     6     private final String code;
     7     private final String label;
     8 
     9     RefNodeType(String code, String label) {
    10         this.code = code;
    11         this.label = label;
    12     }
    13 
    14     public String getCode() {
    15         return code;
    16     }
    17 
    18     public String getLabel() {
    19         return label;
    20     }
    21 
    22     @Override // 需要实现getValue方法
    23     public Object getValue() {
    24         return code;
    25     }
    26 }

    参数校验加上@EnumCheck枚举校验注解,如下所示:

    1 public class TestDto {
    2   @NotBlank(message = "变量类型不能为空")
    3   @EnumCheck(clazz = RefNodeType.class, message = "变量类型不合法") // 加上枚举校验注解
    4   private String sourceType;
    5 }

    正则通用校验

    1)定义RegRule接口,将需要用到的正则表达式定义为接口属性,如下所示:

    1 public interface RegRule {
    2     String MOBILE = "^(((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[6])|(17[0135678])|(18[0-9])|(19[89]))\d{8})$";
    3 }

    2)对象属性加上@Pattern注解,如下所示:

    1 public class UserDto {
    2   @Pattern(regexp = RegRule.MOBILE, message = "手机号格式不正确")
    3   private String mobile;
    4 }

     各类异常捕获处理

    参数校验失败后会抛出异常,我们只需要在全局异常处理类中捕获参数校验的失败异常,然后将错误消息添加到返回值中即可。捕获异常的方法如下所示,返回值Result是我们系统自定义的返回值类。

     1 @RestControllerAdvice
     2 public class GlobalExceptionHandler {
     3      /**
     4       * 全局异常处理
     5       */
     6      @ExceptionHandler(Exception.class)
     7      public Result handleException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
     8          // do something
     9      }
    10 }

    1)缺少参数抛出的异常是MissingServletRequestParameterException,如下所示:

    1 if (e instanceof MissingServletRequestParameterException){
    2     Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
    3     String msg = MessageFormat.format("缺少参数{0}", ((MissingServletRequestParameterException) e).getParameterName());
    4     result.setMessage(msg);
    5     return result;
    6 }

    2)单参数校验失败后抛出的异常是ConstraintViolationException,如下所示:

     1 if (e instanceof ConstraintViolationException){
     2   Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
     3   Set<ConstraintViolation<?>> sets = ((ConstraintViolationException) e).getConstraintViolations();
     4   if(CollectionUtils.isNotEmpty(sets)){
     5     StringBuilder sb = new StringBuilder();
     6     sets.forEach(error -> {
     7                     if (error instanceof FieldError) {
     8                         sb.append(((FieldError)error).getField()).append(":");
     9                     }
    10                     sb.append(error.getMessage()).append(";");
    11                 });
    12     String msg = sb.toString();
    13     msg = StringUtils.substring(msg, 0, msg.length() -1);
    14     result.setMessage(msg);
    15   }
    16   return result;
    17 }

    3)get请求的对象参数校验失败后抛出的异常是BindException,如下所示:

     1 if (e instanceof BindException){
     2       Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
     3       List<ObjectError> errors = ((BindException) e).getBindingResult().getAllErrors();
     4       String msg = getValidExceptionMsg(errors);
     5       if (StringUtils.isNotBlank(msg)){
     6         result.setMessage(msg);
     7       }
     8       
     9       return result;
    10 }
    11 
    12 private String getValidExceptionMsg(List<ObjectError> errors) {
    13   if(CollectionUtils.isNotEmpty(errors)){
    14     StringBuilder sb = new StringBuilder();
    15     errors.forEach(error -> {
    16                     if (error instanceof FieldError) {
    17                        sb.append(((FieldError)error).getField()).append(":");
    18                     }
    19                     sb.append(error.getDefaultMessage()).append(";");
    20                 });
    21     String msg = sb.toString();
    22     msg = StringUtils.substring(msg, 0, msg.length() -1);
    23     return msg;
    24   }
    25   return null;
    26 }

    4)post请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException,如下所示:

     1 if (e instanceof MethodArgumentNotValidException){
     2       Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
     3       List<ObjectError> errors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();
     4       String msg = getValidExceptionMsg(errors);
     5       if (StringUtils.isNotBlank(msg)){
     6         result.setMessage(msg);
     7       }
     8       
     9       return result;
    10 }

  • 相关阅读:
    【学习总结】Git学习-参考廖雪峰老师教程二-安装Git
    【学习总结】Git学习-参考廖雪峰老师教程一-Git简介
    【学习总结】vi/vim命令是使用
    【学习总结】Git学习-参考廖雪峰老师教程-总
    【kindle笔记】之 《鬼吹灯》-9-20
    【学习总结】win7下安装Ubuntu双系统的日常
    【kindle笔记】之 《明朝那些事儿》-2018-7-1
    【kindle笔记】之 《黑客微百科》-2018-6-17
    【kindle笔记】之 《恶意》-2018-4-20
    【kindle笔记】之 《活着》-2018-2-5
  • 原文地址:https://www.cnblogs.com/itsharehome/p/15425885.html
Copyright © 2011-2022 走看看