zoukankan      html  css  js  c++  java
  • 【java框架】SpringBoot(9) -- Springboot中如何优雅的进行字段校验

    参数校验放置位置:一般推荐与业务无关的放在Controller层中进行校验,而与业务有关的放在Service层中进行校验。如果项目中都在Controller层中进行的校验,则按照项目的规范要求参照进行校验即可。

    1.1.常用的校验工具类

    使用Hiberbate Validate

    引入依赖:

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>4.3.1.Final</version>
    </dependency>

    常用注解说明

    注解说明
    @Length(min=,max=) 检查所属的字段的长度是否在min和max之间,只能用于字符串
    @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
    @Max 该字段的值只能小于或等于该值
    @Min 该字段的值只能大于或等于该值
    @NotNull 不能为null
    @NotBlank 不能为空,检查时会将空格忽略
    @NotEmpty 不能为空,这里的空是指空字符串
    @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式

     使用姿势

    需要搭配在Controller中搭配@Validated或@Valid注解一起使用,@Validated和@Valid注解区别不是很大,一般情况下任选一个即可,区别如下:

    注解@Validated@Valid
    所属的包 属于org.springframework.validation.annotation包下的,是spring提供的 属于javax.validation包下,是jdk给提供的
    是否支持分组和排序

     虽然@Validated比@Valid更加强大,在@Valid之上提供了分组功能和验证排序功能,不过在实际项目中一直没有用到过 Hibernate-validate框架中的注解是需要加在实体中一起使用的

    • 定义一个实体
    public class DataSetSaveVO {
        //唯一标识符为空
        @NotBlank(message = "user uuid is empty")
        //用户名称只能是字母和数字
        @Pattern(regexp = "^[a-z0-9]+$", message = "user names can only be alphabetic and numeric")
        @Length(max = 48, message = "user uuid length over 48 byte")
        private String userUuid;
    
        //数据集名称只能是字母和数字
        @Pattern(regexp = "^[A-Za-z0-9]+$", message = "data set names can only be letters and Numbers")
        //文件名称过长
        @Length(max = 48, message = "file name too long")
        //文件名称为空
        @NotBlank(message = "file name is empty")
        private String name;
    
        //数据集描述最多为256字节
        @Length(max = 256, message = "data set description length over 256 byte")
        //数据集描述为空
        @NotBlank(message = "data set description is null")
        private String description;
    }

    说明:message字段为不符合校验规则时抛出的异常信息

    • Controller层中的方法
    @PostMapping
    public ResponseVO createDataSet(@Valid @RequestBody DataSetSaveVO dataSetVO) {
        return ResponseUtil.success(dataSetService.saveDataSet(dataSetVO));
    }

    说明:在校验的实体DataSetSaveVO旁边添加@Valid或@Validated注解

    1.2.使用commons-lang3

    引入依赖

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.4</version>
    </dependency>

    常用方法说明

    方法说明
    CollectionUtils.isEmpty 判断集合是否为空,为null或者size==0,返回true
    CollectionUtils.isNotEmpty 判断集合是否为非空
    StringUtils.isEmpty 判断字符串是否为空
    StringUtils.isNotEmpty 判断字符串是否非空
    StringUtils.isBlank 判断字符串是否为空,为null或者size==0或者只存在空白字符(如" "),则返回true
    StringUtils.isNotBlank 判断字符串是否为非空

     测试代码

    //StringUtils.isEmpty
    System.out.println(StringUtils.isEmpty(""));  //true
    System.out.println(StringUtils.isEmpty("  "));  //false
    //StringUtils.isNotEmpty
    System.out.println(StringUtils.isNotEmpty(""));  //false
    
    //StringUtils.isBlank
    System.out.println(StringUtils.isBlank(""));  //true
    System.out.println(StringUtils.isBlank(" "));  //true
    //StringUtils.isNotBlank
    System.out.println(StringUtils.isNotBlank(" "));  //false
    
    List<Integer> emptyList = new ArrayList<>();
    List<Integer> nullList = null;
    List<Integer> notEmptyList = new ArrayList<>();
    notEmptyList.add(1);
    
    //CollectionUtils.isEmpty
    System.out.println(CollectionUtils.isEmpty(emptyList));   //true
    System.out.println(CollectionUtils.isEmpty(nullList));   //true
    System.out.println(CollectionUtils.isEmpty(notEmptyList));   //false
    
    //CollectionUtils.isNotEmpty
    System.out.println(CollectionUtils.isNotEmpty(emptyList));   //false
    System.out.println(CollectionUtils.isNotEmpty(nullList));   //false
    System.out.println(CollectionUtils.isNotEmpty(notEmptyList));   //true

    1.3. Controller中统一异常拦截处理

    以下是两个简单的在Controller中可以设置的异常捕获类,当代码在Service层中可以throw或者异常抛出的时候进行拦截处理:

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public JsonResponse<Object> resloveConstraintViolationExcption(ConstraintViolationException ex, HttpServletResponse response){
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if(!CollectionUtils.isEmpty(constraintViolations)){
            StringBuilder msgBuilder = new StringBuilder();
            for (ConstraintViolation<?> constraintViolation : constraintViolations) {
                msgBuilder.append(constraintViolation.getMessage()).append(",");
            }
            String errorMessage = msgBuilder.toString();
            if(errorMessage.length() > 1){
                errorMessage = errorMessage.substring(0, errorMessage.length()-1);
            }
            LOGGER.error("ConstranintViolationException:error{}", errorMessage);
            response.setStatus(JsonResponse.FAILED_STATUS);
            return new JsonResponse<>(JsonResponse.FAILED_STATUS, "请求参数有误:" + errorMessage);
        }
    
        LOGGER.error("ConstranintViolationException:error{}", ex.getMessage());
        response.setStatus(JsonResponse.FAILED_STATUS);
        return new JsonResponse<>(JsonResponse.FAILED_STATUS, "请求参数有误:" + ex.getMessage());
    }
    @ExceptionHandler(NumberFormatException.class)
    @ResponseBody
    public JsonResponse<Object> resolveNumberFormatException(NumberFormatException ex, HttpServletResponse response){
        LOGGER.error("NumberFormatException:error{}", ex.getMessage());
        response.setStatus(JsonResponse.FAILED_STATUS);
        return new JsonResponse<>(JsonResponse.FAILED_STATUS, "转换Number处理问题有误:" + ex.getMessage());
    }

    1.4.使用Validated与@Valid做参数校验 

    @Valid是使用Hibernate validation的时候使用;@Validated是Spring Validator做校验机制时使用

    说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现。

    注解位置:

    @Validated:用在类、方法和方法参数上,但不能用于成员属性(fileld)上,会编译报错,嵌套校验必须使用@Valid一起加在成员属性上。

    @Valid:可以用在方法、构造函数、方法参数和成员属性(field)上,用在类上面编译不报错,但是没用。

    分组校验

    @Validated:提供分组功能,可以在参数验证时,根据不同的分组采用不同的验证机制

    @Valid:没有分组功能

    @Getter
    @Setter
    public class RequestParam {
        @NotBlank(groups = {Insert.class})
        private String paramInsert;
        @NotBlank(groups = {Update.class})
        private String paramUpdate;
    
        private Order order;
    
        public interface Insert {
        }
    
        public interface Update {
        }
    }

    请求Controller:

    @RestController
    @RequestMapping("/param")
    @Validated
    public class ParamValidController {
        @PostMapping("/insert")
        public void insert(@RequestBody @Validated({RequestParam.Insert.class}) Order order){
        }
    
        @PostMapping("/update")
        public void update(@RequestBody @Validated({RequestParam.Update.class}) Order order{}
    }

    这样就可以实现对insert方法校验paramInsert,对update方法校验paramUpdate。

    嵌套校验

    @Getter
    @Setter
    public class RequestParam {
        @NotBlank(groups = {Insert.class})
        private String paramInsert;
        @NotBlank(groups = {Update.class})
        private String paramUpdate;
        @Valid
        @NotNull
        private Order order;
    
        public interface Insert {
        }
    
        public interface Update {
        }
    }
    @Data
    public class Order {
        @NotBlank
        private String orderName;
        @Pattern(regexp = "[0-9A-Za-z]{5}", message = "设备编码必须符合数字与字母的规则")
        private Integer orderNo;
    }

    一个待验证的pojo类,如果其中还包含了待验证的对象,需要在待验证对象上注解@Valid,才能验证待验证对象中的成员属性。

    此处@NotNull只能验证order不为空,加上了@Valid才能对Order嵌套类的成员属性进行验证,当然在Order类中的需校验的属性字段也需要添加校验注解。

     

    总结:

    • 在使用@Validated做参数校验的时候,需要在Controller类上面加@Validated注解,参数校验才能生效,如果方法参数是pojo类,则需要在参数上面也加@Validated或@Valid校验才能生效。非pojo类的参数,不需要加@Validated或@Valid。

    • 可以使用@Validated({RequestParam.Insert.class})实现同一个pojo类的分组校验
    • 需要对待验证的pojo类内部的对象属性进行验证的时候,需要在待验证的对象上注解@Valid,实现嵌套验证

    另外推荐两篇写的比较好的SpringBoot项目做参数校验的博文:

    Spring Boot 参数校验

    @Validated和@Valid区别

    博文参考:

    Springboot中如何优雅的进行字段校验

  • 相关阅读:
    SAP S/4HANA extensibility扩展原理介绍
    SAP CRM系统订单模型的设计与实现
    使用nodejs代码在SAP C4C里创建Individual customer
    SAP Cloud for Customer Account和individual customer的区别
    Let the Balloon Rise map一个数组
    How Many Tables 简单并查集
    Heap Operations 优先队列
    Arpa’s obvious problem and Mehrdad’s terrible solution 思维
    Passing the Message 单调栈两次
    The Suspects 并查集
  • 原文地址:https://www.cnblogs.com/yif0118/p/15077815.html
Copyright © 2011-2022 走看看