天天看点

从零搭建 Spring Boot 后端项目(十)

简介

在 RESTful 的接口传参时,我们不能信任任何用户输入,所以开发时要进行数据校验。例如经常要写判断字段是否为空,长度限制等,这些代码繁琐,冗长,还容易出错,这里我们使用 Hibernate-Validator 来解决数据校验问题,会使问题的解决方式优雅很多。那么什么是Hibernate-Validator呢,一开始Java规定了一套关于验证器的接口,即Bean Validation(JSR 303 和 JSR 349)。Bean Validation并不是一项技术而是一种规范,需要对其实现。这里hibernate团队就提供了参考实现,即是Hibernate-Validator

常用注解
@AssertFalse Boolean,boolean 验证注解的元素值是false
@AssertTrue Boolean,boolean 验证注解的元素值是true
@NotNull 任意类型 验证注解的元素值不是null
@Null 任意类型 验证注解的元素值是null
@Min(value=值) BigDecimal,BigInteger,byte,short,int,long等任何Number子类型 验证注解的元素值大于等于@Min指定的value值
@Max(value=值) BigDecimal,BigInteger,byte,short,int,long等任何Number子类型 验证注解的元素值小于等于@Max指定的value值
@DecimalMin(value=值) BigDecimal,BigInteger,byte,short,int,long等任何Number子类型 验证注解的元素值大于等于@DecimalMin指定的value值
@DecimalMax(value=值) BigDecimal,BigInteger,byte,short,int,long等任何Number子类型 验证注解的元素值小于等于@DecimalMax指定的value值
@Digits(integer=整数位数,fraction=小数位数) BigDecimal,BigInteger,byte,short,int,long等任何Number子类型 验证注解的元素值的整数位数和小数位数上限
@Size(min=下限,max=上限) 字符串,Collection. Map,数组等 验证注解的元素值的在min和max (包含)指定区间之内,如字符长度、集合大小
@Past java.util.Date,java.util.Calendar,Joda Time类库的日期类型 验证注解的元素值(日期类型)比当前时间早
@Future java.util.Date,java.util.Calendar,Joda Time类库的日期类型 验证注解的元素值(日期类型)比当前时间晚
@NotBlank CharSequence子类型 验证注解的元素值不为空(不为null、 去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
@Length(min=下限,max=上限) CharSequence子类型 验证注解的元素值长度在min和max区间内
@NotEmpty CharSequence子类型,Collection,Map, 数组 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值,max=最大值) BigDecimal,Biglnteger,CharSequence,byte,short,int,long等原子类型和包装类型 验证注解的元素值在最小值和最大值之间
@Email(regexp=正则表达式,flag=标志的模式) CharSequence子类型(如String) 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式
@Pattern(regexp=正则表达式,flag=标志的模式) String,任何CharSequence的子类型 验证注解的元素值与指定的正则表达式匹配
@Valid 任何非原子类型 指定递归验证关联的对象,如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证

注:@NotNull,@NotBlank与 @NotEmpty 的用法区别: int 、long基础类型,和Integer、Long等包装类型判断非空时使用 @NotNull,String 类型字符串一般用 @NotBlank,而集合类一般用 @NotEmpty。但具体问题要具体分析,比如为String时允许为空字符串"",不允许为null时,需要用**@NotNull**

除了这些注解,我们还可以自定义校验注解,以应对更多的业务场景。需自定义一个注解与实现ConstraintValidator接口,这里不再过多拓展,有需要自行查找即可

VO、DTO与DO
  • DO(Data Object):数据对象,类里的每一个字段,与数据库相对应,即实体类
  • DTO(Data Transfer Object):数据传输对象,展示层与服务层之间的数据传输对象
  • VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来

    大多数情况下DTO和VO的属性值,基本是一致的,所以很多时候后端在实现层面上,就没有写VO。但是在设计层面,两者有着本质的区别:DTO代表服务层需要接收和返回的数据,而VO代表展示层需要显示的数据。例如:0表示男,1表示女,在DTO层就是0和1,但是VO层就可能是0和1,男和女,帅哥和美女,靓仔和靓妹。这里我们用的是 RESTful 风格写的API 相当于只传入或传出DTO,前后端分离,即等于前端帮我们把DTO转成了相应的VO。如果是 MVC 模式,视图与数据相绑定,这时就需要我们再加一层VO了。再比如某个框架(如Flex)提供自动把POJO转换为UI中某些Field时,可以考虑在实现层面定义出VO。总结:设计层面一定要有VO的情况,实际后端服务层实现用不用VO层视情况而定

步骤

  • spring boot 2.2 版本以前的依赖spring-boot-starter-web中包含了Hibernate-Validator依赖,但spring boot 2.2以后就不包含了,这里要手动添加到pom.xml
    <!-- hibernate-validator -->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>6.1.0.Final</version>
            </dependency>
               
  • 这里我们模拟一次正常的接口请求,然后在此过程中,对传入的参数进行数据校验,首先还是建一张数据表
    create table employee
      (
          id int not NULL AUTO_INCREMENT,
          name varchar(255) not null,
          gender int not null,
          date_of_birth datetime null,
          phone varchar(255) null,
          email varchar(255) null,
          dept varchar(255) null,
          constraint goods_pk
              primary key (id)
      );
    
    INSERT INTO employee (name, gender, date_of_birth, phone, email, dept) VALUES ('nail', 0, '1988-10-01', '18945174678', '[email protected]', 'Development');
    INSERT INTO employee (name, gender, date_of_birth, phone, email, dept) VALUES ('bob', 0, '1990-05-01 00:00:00', '18647815474', '[email protected]', 'Test');
               
  • 在数据表上方右键依次按如下步骤开始操作,我们开始生成代码
    从零搭建 Spring Boot 后端项目(十)
  • 在Package下填写

    com.example.backend_template

    ,然后选择要生成的六个文件,最后点击OK键,以生成六个文件,如果想看更详细的步骤,可以看这篇文章代码自动生成
    从零搭建 Spring Boot 后端项目(十)
  • 运行项目,用 Postman 访问 http://localhost:8080/employee/selectOne?id=1 ,如出现如下数据,则说明自动生成代码成功,才能接着做其它的步骤
    从零搭建 Spring Boot 后端项目(十)
  • 接下来我们在自动生成的代码基础上,增加新的接口,这里我们首先新增一个xxxDTO,在此xxxDTO上做数据校验,在com.example.backend_template.dto新建两个文件夹一个request,一个response,并在request文件夹下新增EmployeeDTO.java类,这里自己写下get和set方法,太多了,这就不写了
    package com.example.backend_template.dto.request;
    
    import com.fasterxml.jackson.annotation.JsonProperty;
    import org.hibernate.validator.constraints.Range;
    import org.springframework.format.annotation.DateTimeFormat;
    
    import javax.validation.constraints.*;
    import java.util.Date;
    
    /**
     * @ClassName EmployeeDTO
     * @Description
     * @Author L
     * @Date Create by 2020/7/16
     */
    public class EmployeeDTO {
    
        @NotBlank(message = "name must not be null!")
        private String name;
        @NotNull(message = "gender must not be null!")
        @Range(min = 0, max = 1, message = "gender must be one of the following 0 or 1!")
        private Integer gender;
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        @NotNull(message = "date_of_birth must not be null!")
        @Past(message = "date_of_birth has to be past time!")
        @JsonProperty(value = "date_of_birth")
        private Date dateOfBirth;
        private String phone;
        @Email(message = "email format is incorrect! ")
        private String email;
        @NotBlank(message = "dept must not be null!")
        @Pattern(regexp = "Test|Development|Administration", message = "dept must be one of the following Test、Development、Administration!")
        private String dept;
        @NotNull(message = "salary must not be null!")
        private Integer salary;
    
        //get and set...
    }
               
    注:我们假设页面会传以上信息,尽管我们可能不会全存到employee,因为正常开发情况下,它可能传到别的表,或做别的处理
  • 在com.example.backend_template.service.EmployeeService.java类下新加以下接口方法,注意导入相关的类,之后也是
    /**
         *  创建employee相关信息,包括基本信息和工资信息
         *
         * @param dto
         * @return
         */
        EmployeeDTO createEmployeeInfo(EmployeeDTO dto);
               
  • 为了之后方便,先在com.example.backend_template.entity.Employee.java类下新增两个构造方法
    public Employee() {
        }
    
        public Employee(String name, Integer gender, Date dateOfBirth, String phone, String email, String dept) {
            this.id = id;
            this.name = name;
            this.gender = gender;
            this.dateOfBirth = dateOfBirth;
            this.phone = phone;
            this.email = email;
            this.dept = dept;
        }
               
  • 在com.example.backend_template.service.EmployeeServiceImpl.java类下,新增以下实现方法
    @Override
        public EmployeeDTO createEmployeeInfo(EmployeeDTO dto) {
            Employee employee = new Employee(dto.getName(), dto.getGender(),
                    dto.getDateOfBirth(), dto.getPhone(), dto.getEmail(), dto.getDept());
            employeeDao.insert(employee);
            //假设薪水是做别的存储处理
            dto.getSalary();
    
            return dto;
        }
               
  • 在com.example.backend_template.controller.EmployeeController.java类下,新增以下接口, 并将返回结果统一成ResultData<T>数据结构
    /**
         * 创建employee相关信息,包括基本信息和工资信息
         * @param dto
         * @return
         */
        @PostMapping("")
        public ResultData<Object> createEmployeeInfo(@RequestBody @Valid EmployeeDTO dto){
            EmployeeDTO employeeDTO = employeeService.createEmployeeInfo(dto);
            return ResultUtils.success(employeeDTO);
        }
               
  • 启动项目,用Postman对 http://localhost:8080/employee 发起POST请求,参数如下
    {
        "name":"Tom",
        "gender":0,
        "date_of_birth":"1999-10-01",
        "phone":"13615498742",
        "email":"[email protected]",
        "dept":"Development",
        "salary":2000
    }
               
    成功后,结果如下图
    从零搭建 Spring Boot 后端项目(十)
  • 此时传数据的格式不对,就会报参数校验的相关错误,错误很多,可自行尝试,例如,这里使name为空会报如下错误
    {
        "code": 400,
        "msg": "Method Argument Not Valid!",
        "data": "Validation failed for argument [0] in public com.example.backend_template.utils.ResultData<java.lang.Object> com.example.backend_template.controller.EmployeeController.createEmployeeInfo(com.example.backend_template.dto.request.EmployeeDTO): [Field error in object 'employeeDTO' on field 'name': rejected value [null]; codes [NotBlank.employeeDTO.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employeeDTO.name,name]; arguments []; default message [name]]; default message [name must not be null!]] "
    }
               
    结果图如下
    从零搭建 Spring Boot 后端项目(十)
    测试完,知道怎么用后,就可以把这些多余的类删除了

项目地址

项目介绍:从零搭建 Spring Boot 后端项目

代码地址:https://github.com/xiaoxiamo/backend-template

下一篇

十一、全局日志处理

继续阅读