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

小咪咪 2023-02-28 05:53 116阅读 0赞

简介

在 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

    1. <!-- hibernate-validator -->
    2. <dependency>
    3. <groupId>org.hibernate</groupId>
    4. <artifactId>hibernate-validator</artifactId>
    5. <version>6.1.0.Final</version>
    6. </dependency>
  • 这里我们模拟一次正常的接口请求,然后在此过程中,对传入的参数进行数据校验,首先还是建一张数据表

    1. create table employee
    2. (
    3. id int not NULL AUTO_INCREMENT,
    4. name varchar(255) not null,
    5. gender int not null,
    6. date_of_birth datetime null,
    7. phone varchar(255) null,
    8. email varchar(255) null,
    9. dept varchar(255) null,
    10. constraint goods_pk
    11. primary key (id)
    12. );
    13. INSERT INTO employee (name, gender, date_of_birth, phone, email, dept) VALUES ('nail', 0, '1988-10-01', '18945174678', 'nail@163.com', 'Development');
    14. INSERT INTO employee (name, gender, date_of_birth, phone, email, dept) VALUES ('bob', 0, '1990-05-01 00:00:00', '18647815474', 'bob@163.com', 'Test');
  • 在数据表上方右键依次按如下步骤开始操作,我们开始生成代码
    在这里插入图片描述
  • 在Package下填写com.example.backend_template,然后选择要生成的六个文件,最后点击OK键,以生成六个文件,如果想看更详细的步骤,可以看这篇文章代码自动生成在这里插入图片描述
  • 运行项目,用 Postman 访问 http://localhost:8080/employee/selectOne?id=1 ,如出现如下数据,则说明自动生成代码成功,才能接着做其它的步骤
    在这里插入图片描述
  • 接下来我们在自动生成的代码基础上,增加新的接口,这里我们首先新增一个xxxDTO,在此xxxDTO上做数据校验,在com.example.backend_template.dto新建两个文件夹一个request,一个response,并在request文件夹下新增EmployeeDTO.java类,这里自己写下get和set方法,太多了,这就不写了

    1. package com.example.backend_template.dto.request;
    2. import com.fasterxml.jackson.annotation.JsonProperty;
    3. import org.hibernate.validator.constraints.Range;
    4. import org.springframework.format.annotation.DateTimeFormat;
    5. import javax.validation.constraints.*;
    6. import java.util.Date;
    7. /**
    8. * @ClassName EmployeeDTO
    9. * @Description
    10. * @Author L
    11. * @Date Create by 2020/7/16
    12. */
    13. public class EmployeeDTO {
    14. @NotBlank(message = "name must not be null!")
    15. private String name;
    16. @NotNull(message = "gender must not be null!")
    17. @Range(min = 0, max = 1, message = "gender must be one of the following 0 or 1!")
    18. private Integer gender;
    19. @DateTimeFormat(pattern = "yyyy-MM-dd")
    20. @NotNull(message = "date_of_birth must not be null!")
    21. @Past(message = "date_of_birth has to be past time!")
    22. @JsonProperty(value = "date_of_birth")
    23. private Date dateOfBirth;
    24. private String phone;
    25. @Email(message = "email format is incorrect! ")
    26. private String email;
    27. @NotBlank(message = "dept must not be null!")
    28. @Pattern(regexp = "Test|Development|Administration", message = "dept must be one of the following Test、Development、Administration!")
    29. private String dept;
    30. @NotNull(message = "salary must not be null!")
    31. private Integer salary;
    32. //get and set...
    33. }

    注:我们假设页面会传以上信息,尽管我们可能不会全存到employee,因为正常开发情况下,它可能传到别的表,或做别的处理

  • com.example.backend_template.service.EmployeeService.java类下新加以下接口方法,注意导入相关的类,之后也是

    1. /**
    2. * 创建employee相关信息,包括基本信息和工资信息
    3. *
    4. * @param dto
    5. * @return
    6. */
    7. EmployeeDTO createEmployeeInfo(EmployeeDTO dto);
  • 为了之后方便,先在com.example.backend_template.entity.Employee.java类下新增两个构造方法

    1. public Employee() {
    2. }
    3. public Employee(String name, Integer gender, Date dateOfBirth, String phone, String email, String dept) {
    4. this.id = id;
    5. this.name = name;
    6. this.gender = gender;
    7. this.dateOfBirth = dateOfBirth;
    8. this.phone = phone;
    9. this.email = email;
    10. this.dept = dept;
    11. }
  • com.example.backend_template.service.EmployeeServiceImpl.java类下,新增以下实现方法

    1. @Override
    2. public EmployeeDTO createEmployeeInfo(EmployeeDTO dto) {
    3. Employee employee = new Employee(dto.getName(), dto.getGender(),
    4. dto.getDateOfBirth(), dto.getPhone(), dto.getEmail(), dto.getDept());
    5. employeeDao.insert(employee);
    6. //假设薪水是做别的存储处理
    7. dto.getSalary();
    8. return dto;
    9. }
  • com.example.backend_template.controller.EmployeeController.java类下,新增以下接口, 并将返回结果统一成ResultData数据结构

    1. /**
    2. * 创建employee相关信息,包括基本信息和工资信息
    3. * @param dto
    4. * @return
    5. */
    6. @PostMapping("")
    7. public ResultData<Object> createEmployeeInfo(@RequestBody @Valid EmployeeDTO dto){
    8. EmployeeDTO employeeDTO = employeeService.createEmployeeInfo(dto);
    9. return ResultUtils.success(employeeDTO);
    10. }
  • 启动项目,用Postman对 http://localhost:8080/employee 发起POST请求,参数如下

    1. {
    2. "name":"Tom",
    3. "gender":0,
    4. "date_of_birth":"1999-10-01",
    5. "phone":"13615498742",
    6. "email":"tom@163.com",
    7. "dept":"Development",
    8. "salary":2000
    9. }

    成功后,结果如下图
    在这里插入图片描述

  • 此时传数据的格式不对,就会报参数校验的相关错误,错误很多,可自行尝试,例如,这里使name为空会报如下错误

    1. {
    2. "code": 400,
    3. "msg": "Method Argument Not Valid!",
    4. "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!]] "
    5. }

    结果图如下
    在这里插入图片描述
    测试完,知道怎么用后,就可以把这些多余的类删除了

项目地址

项目介绍:从零搭建 Spring Boot 后端项目
代码地址:https://github.com/xiaoxiamo/backend-template

下一篇

十一、全局日志处理

发表评论

表情:
评论列表 (有 0 条评论,116人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring Boot 项目

    简介 一个基于 Spring Boot 的后端开发模板,主要用于减少平时重复的工作量,以及使开发有良好的开发规范,主要功能包括但不限于权限管理、在线接口文档、日志记录、单