SpringBoot系列:Pojo validation
JSR 303 规范了bean validation, Hibernate validator实现了JSR 303所有的规范, 同时也是最常用的validator 工具包.
使用 Hibernate validator 可以大大简化数据验证工作. 对于 Web 项目, 通常前端需要做一些输入验证, 完善的前端验证能给用户很好的使用体验, 但仅有前端验证还是不够的, 后端必须也有相应的验证, 因为前端验证是很容易跳过去的.
有两个数据验证的做法:
方式1. Pojo 上加检查规则, Controller 师徒函数Pojo形参前加上 @Validated 来触发检查动作.
方式2. 在 bean 类(通常是 Service 类) 上加 @Validated注解, 在bean类的方法的形参前增加检查规则, 然后在controller方法中, 通过调用该bean方法将触发检查动作.
===================================
方式1
===================================
使用 Hibernate validator 的步骤:
1. 在 Pojo 类的字段上, 加上 Hibernate validator 注解
2. 在Controller 函数的形参前加上 @Valid 或 @Validated 注解, 触发一次validation.
3. 在每个 @Valid 或 @Validated 注解参数后, 紧跟一个 BindingResult 类型的参数. 如果没有提供对应的 BindingResult 参数, Spring MVC 将抛出异常.
4. 在Controller 函数中, 通过 BindingResult 类型的参数获取检验的结果.
--------------------------------------------------------------------
Pojo validation 规则注解
--------------------------------------------------------------------
参考: https://blog.csdn.net/danielzhou888/article/details/74740817
| @Null | 被注释的元素必须为 null | |
| @NotNull | 被注释的元素必须不为 null | |
| @NotBlank | 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格. | |
| @NotEmpty | 检查约束元素不能为NULL或者是EMPTY. | |
| @AssertTrue | 被注释的元素必须为 true | |
| @AssertFalse | 被注释的元素必须为 false | |
| @Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 | |
| @Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 | |
| @DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 | |
| @DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 | |
| @Size(max, min) | 注释的元素的长度必须在指定的范围内, 支持字符串和集合 | |
| @Length(min=, max=) | 被注释的字符串的大小必须在指定的范围内 | |
| 被注释的元素必须是电子邮箱地址 | ||
| @Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 | |
| @Range(min=, max=) | 被注释的元素必须在合适的范围内 | |
| @Past | 被注释的元素必须是一个过去的日期 | |
| @Future | 被注释的元素必须是一个将来的日期 | |
| @Pattern(value) | 被注释的元素必须符合指定的正则表达式 | |
| @URL(protocol=, host=, port=, regexp=, flags=) |
被注释的字符串必须是一个有效的url | |
| @CreditCardNumber | 被注释的字符串必须通过Luhn校验算法, 银行卡,信用卡等号码一般都用Luhn 计算合法性 |
|
--------------------------------------------------------------------
@Valid 和 @Validated 注解
--------------------------------------------------------------------
@Valid 和 @Validated 都用来触发一次校验, @Valid 是 JSR 303规范的注解, @Validated 是Spring 加强版的注解, 加强的地方有:
1. @Validated 可以设定条件group, 即可以设定校验的条件.
比如对于 Book 类中 bookId 是自增id, 在新增 Book 时候要求为 null, 在更新时要求不能是 null. 在实际编码中, 通常使用几个空的interface 作为 group 参数.
2. @Validated 支持组序列, 该特性用的较少, 参考 https://blog.csdn.net/wangpeng047/article/details/41726299
所以, 推荐使用 @Validated 注解.
===================================
方式2
===================================
在 bean 类(通常是 Service 类) 上加 @Validated注解, 在bean类的方法的形参前增加检查规则, 然后在controller方法中, 通过调用该bean方法将触发检查动作. 如果检查结果失败, 将抛出 ConstraintViolationException 异常.
Service
@Validated
public class SomeService { @Length(min = 3, max = 5)
public String createUser(@NotBlank @Email String email,
@NotBlank String username,
@NotBlank String password) {
return username;
}
} @RestController
@RequestMapping("/")
public class SomeController {
@Autowired
SomeService someService; @GetMapping("/")
@RequestMapping("/")
public hello(){
someService.createUser("email","username","password");
return "Hello";
}
}
====================================
自定义校验规则
====================================
Hibernate invalidator提供的验证规则应该满足大多数情形, 如果愿意, 我们也可以自定义自己的验证规则, 自定义一个校验规则需要定义一个注解和一个校验类, 比如要新增一个加班时长的校验规则.
使用的代码示例:
@WorkOverTime(max=2)
Integer workOverTime;
@WorkOverTime 注解定义的写法:
直接写一个注解语法还是挺难的, 可修改一个已有规则的代码, 比如 @Email. 修改调整的地方有:
1. 在 @interface WorkOverTime 代码中, 设定 @Constraint 参数, @Constraint(validatedBy = { WorkOverTimeValidator.class })
2. 增加 max 属性, 示例代码如下:
int max() default 5;
WorkOverTimeValidator 类的写法:
WorkOverTimeValidator 类应该实现 ConstraintValidator 接口, 该接口是泛型接口.
class WorkOverTimeValidator implements ConstraintValidator<WorkOverTime, Integer>{
}
====================================
示例代码
====================================
----------------------------------
pom.xml
----------------------------------
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
----------------------------------
validation group 接口
----------------------------------
定义三个 validation 分组 group 接口, 用来区分新增/更新/删除操作.
interface AddEntity {
}
interface UpdateEntity {
}
interface DeleteEntity {
}
----------------------------------
POJO 类
----------------------------------
public class Book {
@Null(message = "bookId should be null when add", groups = { AddEntity.class })
@NotNull(message = "bookId should be not null when update and delete", groups = { UpdateEntity.class, DeleteEntity.class })
private Integer bookId;
private String author;
public Integer getBookId() {
return bookId;
}
public void setBookId(Integer bookId) {
this.bookId = bookId;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
----------------------------------
Controller 类
----------------------------------
@RestController
@RequestMapping("/bookApi")
class BookController { // 新增操作
// Book 参数要加上 @RequestBody
// book 参数加上 @Validated 验证指令
@PostMapping("/books")
public Book addBook(@Validated({ AddEntity.class }) @RequestBody Book book, BindingResult bookBinding) {
if (bookBinding.hasErrors()) {
List<ObjectError> errors = bookBinding.getAllErrors();
errors.stream()
.map(e -> e.getObjectName() + "," + e.getDefaultMessage())
.forEach(System.out::println);
return null;
}
book.setBookId(100);
return book;
} // 更新操作
@PutMapping("/books/{bookId}")
public Book updateBook(@Validated({ UpdateEntity.class }) @RequestBody Book book, BindingResult bookBinding) {
if (bookBinding.hasErrors()) {
List<ObjectError> errors = bookBinding.getAllErrors();
errors.stream()
.map(e -> e.getObjectName() + "," + e.getDefaultMessage())
.forEach(System.out::println);
return null;
}
return book;
} // list all 操作
@GetMapping("/books")
public List<Book> listBooks() {
List<Book> books = new ArrayList<Book>();
Book book;
book = new Book();
book.setBookId(20);
books.add(book);
return books;
}
}
====================================
演示截图
====================================
1. Post 请求 http://localhost:8080/bookApi/books, 输入的 Book对象包含 bookId, 输入截图

服务器端报错截图:

1. Post 请求 http://localhost:8080/bookApi/books, 输入的 Book对象不包含 bookId, 验证通过.

====================================
参考
====================================
主要参考李家智的书 <<Spring Boot 2精髓>>
SpringBoot系列:Pojo validation的更多相关文章
- springBoot系列教程07:异常捕获
发生异常是很正常的事,异常种类也是千奇百怪,发生异常并不可怕,只要正确的处理,并正确的返回错误信息并无大碍,如果不进行捕获或者处理,分分钟服务器宕机是很正常的事 所以处理异常时,最基本的要求就是发生异 ...
- Springboot 系列(十二)使用 Mybatis 集成 pagehelper 分页插件和 mapper 插件
前言 在 Springboot 系列文章第十一篇里(使用 Mybatis(自动生成插件) 访问数据库),实验了 Springboot 结合 Mybatis 以及 Mybatis-generator 生 ...
- SpringBoot系列——Spring-Data-JPA(究极进化版) 自动生成单表基础增、删、改、查接口
前言 我们在之前的实现了springboot与data-jpa的增.删.改.查简单使用(请戳:SpringBoot系列——Spring-Data-JPA),并实现了升级版(请戳:SpringBoot系 ...
- SpringBoot系列: RestTemplate 快速入门
====================================相关的文章====================================SpringBoot系列: 与Spring R ...
- SpringBoot系列教程JPA之新增记录使用姿势
SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第 ...
- SpringBoot系列——事件发布与监听
前言 日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱.短信提示用户,通常我们都是这样写: /** * 用户注册 */ @GetMapping("/userRegi ...
- SpringBoot系列——利用系统环境变量与配置文件的分支选择实现“智能部署”
前言 通过之前的博客:SpringBoot系列——jar包与war包的部署,我们已经知道了如果实现项目的简单部署,但项目部署的时候最烦的是什么?修改成发布环境对应的配置!数据库连接地址.Eureka注 ...
- Springboot 系列(九)使用 Spring JDBC 和 Druid 数据源监控
前言 作为一名 Java 开发者,相信对 JDBC(Java Data Base Connectivity)是不会陌生的,JDBC作为 Java 基础内容,它提供了一种基准,据此可以构建更高级的工具和 ...
- SpringBoot系列——Spring-Data-JPA
前言 jpa是ORM映射框架,更多详情,请戳:apring-data-jpa官网:http://spring.io/projects/spring-data-jpa,以及一篇优秀的博客:https:/ ...
随机推荐
- ValueOf()和toString()
var colors = ["red", "blue", "green"]; // 创建一个包含3 个字符串的数组 console.log( ...
- flex布局justify-content属性和align-items属性设置
前言: flex最常用的就是justify-content和align-items了,这里把这两个属性介绍下,大家更多关于flex布局可以查看阮一峰的日志,写的非常清楚! 阮一峰flex布局的日志:h ...
- CF786B Legacy(线段树优化建图)
嘟嘟嘟 省选Day1T2不仅考了字符串,还考了线段树优化建图.当时不会,现在赶快学一下. 线段树能优化的图就是像这道题一样,一个点像一个区间的点连边,或一个区间像一个点连边.一个个连就是\(O(n ^ ...
- logrotate日志处理
介绍 logrotate旨在简化生成大量日志文件的系统的管理.它允许日志文件的自动轮换.压缩.删除和邮件.每个日志文件可以每天.每周.每月处理,也可以在它变得太大时处理.通常,logrotate作为每 ...
- C#中的Finalize,Dispose,SuppressFinalize(转载)
MSDN建议按照下面的模式实现IDisposable接口: public class Foo : IDisposable { public void Dispose() { Dispose(true) ...
- 基于 HTML5 的工业互联网云平台监控机房 U 位
前言 机柜 U 位管理是一项突破性创新技术--继承了 RFID 标签(电子标签)的优点的同时,完全解决了 RFID 技术(非接触式的自动识别技术)在机房 U 位资产监控场应用景中的四大缺陷,采用工业互 ...
- 《React Native 精解与实战》书籍连载「React Native 源码学习方法及其他资源」
此系列文章将整合我的 React 视频教程与 React Native 书籍中的精华部分,给大家介绍 React Native 源码学习方法及其他资源. 最后的章节给大家介绍 React Native ...
- java常识
什么是java语言? java语言是美国Sun公司,在1995年推出的高级编程语言.所谓编程语言,是计算机的语言,人们可以使用编程语言对计算机下达命令,让计算机完成人们需要的功能. java语言发展历 ...
- iview table行render渲染不同的组件
table不同的行,相同的列渲染不同的组件,如图1:第一行渲染selece,第二行渲染input render:(h,params)=>{ if(params.index === 0){ //以 ...
- jmeter学习记录--08--第三方测试组件
我们安装的JMeter版本,功能仍然有欠缺,插件是一种补充,官方提供了很多插件. 官网地址:http://www.jmeter-plugins.org/, 里面有很多可以安装到JMeter的插件,基本 ...