Spring Boot中的校验-Validation的使用

简介: Spring Boot中的校验-Validation的使用

实际业务中,我们是离不开数据的校验的。比如注册用户,用户名和密码是不能为空的。

今天的分享,我用一个简单的使用 Spring Boot+MyBatis 程序添加用户为例,来进行讲解。

1,平常我们进行数据校验

例如这个添加用户的查询,我们要将用户类User的实例添加进数据库,代码如下:

首先是User类:

packagecom.example.validationtest.dataobject;
importlombok.Getter;
importlombok.NoArgsConstructor;
importlombok.Setter;
importjava.io.Serializable;
/*** 用户类*/@Setter@Getter@NoArgsConstructorpublicclassUserimplementsSerializable {
/*** 主键id*/privateIntegerid;
/*** 用户名*/privateStringusername;
/*** 密码*/privateStringpassword;
/*** 邮箱*/privateStringemail;
/*** 年龄*/privateintage;
}

我们会在Service层里面调用dao来把数据添加进数据库,不过我们会在Service类里面写很多校验字段的东西,例如这个添加用户的方法片段:

@AutowiredprivateUserDAOuserDAO;
@OverridepublicStringadd(Useruser) {
if (StringUtils.isEmpty(user.getUsername())) {
return"用户名不能为空!";
   }
if (StringUtils.isEmpty(user.getPassword())) {
return"密码不能为空!";
   }
   ...
userDAO.add(user);
return"添加成功!";
}

最后再使用Controller处理请求进行添加。

这样的逻辑没有错,但是当实体类多起来了,那就很麻烦了,要对每个字段进行if判断校验,不仅麻烦,而且代码冗余。

因此我们需要用到Spring Validation来解决这个问题。

2,配置Validation

在新建Spring Boot工程的时候,我们可以勾上Validation这个依赖。

网络异常,图片无法展示
|

也可以后续手动在pom.xml里面添加依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

3,使用注解对实体类的字段设定校验规则

Validation提供了很多校验规则注解,将其注解在字段上以设定其校验规则,这里列出几个常用的:

  • @NotEmpty 对象不允许为null或者空,例如字符串长度为0,集合、Map等对象长度为0,都判为空
  • @NotBlank 不允许为null和纯空格
  • @NotNull 不允许为空对象
  • @AssertTrue 值是否为true
  • @Size 约定字符串长度,其中参数min表示约定最短长度,max为最大长度
  • @Min 规定数值型属性的最小值
  • @Max 规定数值型属性的最大值
  • @Email 字符串必须为邮箱格式

每个校验都可以累加,一旦一个类中有校验失败,就会记录其中校验错误。每个校验都有message参数,可以自定义校验失败时返回的内容。

建议大多数时候使用@NotEmpty注解代替@NotBlank和@NotNull,不过校验数值类型时,还是需要使用@NotNull。

还是接着上面例子,我们将上述User类修改如下:

packagecom.example.validationtest.dataobject;
importlombok.Getter;
importlombok.NoArgsConstructor;
importlombok.Setter;
importjavax.validation.constraints.*;
importjava.io.Serializable;
/*** 用户类*/@Setter@Getter@NoArgsConstructorpublicclassUserimplementsSerializable {
/*** 主键id*/privateIntegerid;
/*** 用户名*/@NotEmpty(message="用户名不能为空!")
privateStringusername;
/*** 密码*/@Size(min=8, message="密码长度不能小于8!")
@NotEmpty(message="密码不能为空!")
privateStringpassword;
/*** 邮箱*/@Email(message="邮箱格式错误!")
privateStringemail;
/*** 年龄*/@Min(value=18, message="年龄不能小于18!")
@Max(value=150, message="年龄不能小于150!")
privateintage;
}

可以看看字段上的注解的使用,一般设定参数message自定义错误消息,还有value参数规定相应校验值等等。

有了这些校验注解,我们就不用再在Service里面写if去进行判断了!

4,编写Controller类并设定传入的User对象需要校验

现写一简易的Controller方法,接收POST请求传入User实例,传入时指定其需要校验,如果校验失败返回校验信息,成功则调用Service层添加用户(这里省略Service代码)代码如下:

@PostMapping("/add")
publicStringadd(@RequestBody@ValidUseruser, BindingResulterrors) {
if (errors.hasErrors()) {
returnerrors.getFieldError().getDefaultMessage();
   }
userService.add(user);
return"添加成功!";
}

注意这个Controller方法的参数,传入的User对象前面打了@Valid注解,旨在告诉Controller这个需要校验,且比起平常这里多了个参数为BindingResult对象,这个对象用于存放校验错误信息。一般通过BindingResult对象的hasErrors方法判断是否有错误,如果有错误,通过errors.getFieldError().getDefaultMessage()获取错误信息(上文errors就是我们的BindingResult对象)。例如我现在使用Postman工具发送请求数据体如下:

网络异常,图片无法展示
|

结果:

网络异常,图片无法展示
|

可见使用Validation的注解可以轻松实现校验,而不再需要我们逐个手写逻辑

5,优化校验规则-分组校验

同样是用户对象,我们可能在不同情景下,有着不同的校验规则。例如:

  • 用户id通常是自增主键,因此注册用户是传入的用户id字段可以为空,但是用户名密码等等不能为空
  • 然而,更新/修改用户信息时是根据用户id找到用户的,这时传入的用户id又不能为空了,但是用户名密码可以为空(一般来说,前端传入用户对象修改用户信息时,空的字段代表这个字段不需要修改保持原值,这时后端将空的字段用原来的值填上并写进数据库即可)

对于不同的校验规则,我们可以为其分别设定校验规则,并在不同的API使用不同的校验规则。

校验规则的参数要填的是,因此我们用空的接口表示校验规则(空接口可以理解为不同校验规则组的名字),这里我创建param软件包,在里面建立类ValidationRules,并在类中写两个空接口分别表示用户注册、修改的校验规则:

packagecom.example.validationtest.param;
/*** 校验规则类*/publicclassValidationRules {
/*** 注册(添加)用户规则*/publicinterfaceUserAdd {
   }
/*** 更新(修改)用户规则*/publicinterfaceUserUpdate {
   }
}

然后,我们再修改User类如下:

packagecom.example.validationtest.dataobject;
importcom.example.validationtest.param.ValidationRules;
importlombok.Getter;
importlombok.NoArgsConstructor;
importlombok.Setter;
importjavax.validation.constraints.*;
importjava.io.Serializable;
/*** 用户类*/@Setter@Getter@NoArgsConstructorpublicclassUserimplementsSerializable {
/*** 主键id,将其设定分组为ValidationRules.UserUpdate,表示用户信息修改时校验该规则*/@NotNull(groups=ValidationRules.UserUpdate.class, message="用户id不能为空!")
privateIntegerid;
/*** 用户名,将其设定分组为ValidationRules.UserAdd,表示添加用户时校验该规则*/@NotEmpty(groups=ValidationRules.UserAdd.class, message="用户名不能为空!")
privateStringusername;
/*** 密码,将长度校验规则同时加入到ValidationRules.UserAdd和ValidationRules.UserUpdate组,表示添加用户和用户信息修改时都要校验这个规则,空值校验只有添加时校验*/@Size(groups= {ValidationRules.UserAdd.class, ValidationRules.UserUpdate.class}, min=8, message="密码长度不能小于8!")
@NotEmpty(groups=ValidationRules.UserAdd.class, message="密码不能为空!")
privateStringpassword;
// 下面都是一回事/*** 邮箱*/@Email(groups= {ValidationRules.UserAdd.class, ValidationRules.UserUpdate.class}, message="邮箱格式错误!")
privateStringemail;
/*** 年龄*/@Min(groups= {ValidationRules.UserAdd.class, ValidationRules.UserUpdate.class}, value=18, message="年龄不能小于18!")
@Max(groups= {ValidationRules.UserAdd.class, ValidationRules.UserUpdate.class}, value=150, message="年龄不能小于150!")
privateintage;
}

可见,我们在每个校验注释中加上了groups参数,这个参数就是用于指定每个校验规则的分组。这个参数值必须是类class类型,用我们创建的空接口即可,就代表给这个规则指定分组在哪个类。一个校验规则不仅仅可以只给它指定一个分组,也可以指定多个分组(参数值写成数组)。

然后现在编写Controller类,给不同的接口应用不同的规则:

packagecom.example.validationtest.api;
importcom.example.validationtest.dataobject.User;
importcom.example.validationtest.param.ValidationRules;
importorg.springframework.validation.BindingResult;
importorg.springframework.validation.annotation.Validated;
importorg.springframework.web.bind.annotation.PostMapping;
importorg.springframework.web.bind.annotation.RequestBody;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
/*** 测试校验(这里不做实际的用户添加删除工作,只是测试validation校验)*/@RestController@RequestMapping("/api/user")
publicclassUserAPI {
/*** 模拟添加用户,这里设置校验规则为ValidationRules.UserAdd*/@PostMapping("/add")
publicStringadd(@RequestBody@Validated(ValidationRules.UserAdd.class) Useruser, BindingResulterrors) {
if (errors.hasErrors()) {
returnerrors.getFieldError().getDefaultMessage();
      }
return"添加成功!";
   }
/*** 模拟修改用户,这里设置校验规则为ValidationRules.UserUpdate*/@PostMapping("/update")
publicStringupdate(@RequestBody@Validated(ValidationRules.UserUpdate.class) Useruser, BindingResulterrors) {
if (errors.hasErrors()) {
returnerrors.getFieldError().getDefaultMessage();
      }
return"修改成功!";
   }
}

测试添加接口:

网络异常,图片无法展示
|

同样拿这个数据测试修改接口:

网络异常,图片无法展示
|

这里在Controller中,我们这里换用了@Validated注解实现指定分组校验规则进行校验,其中参数值也是代表分组的类(也可以写多个,用数组表示),上述add接口指定了@Validated参数为ValidationRules.UserAdd.class,那么这个接口只会去校验用户类中,设定了groups参数值包含ValidationRules.UserAdd.class的规则。

可见分组校验是一个非常好用方便的功能。

示例仓库地址

相关文章
|
22天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
38 0
|
3月前
|
Java 数据库连接 Spring
Spring之数据校验:Validation
【1月更文挑战第17天】 一、Spring Validation概述 二、实验一:通过Validator接口实现 三、实验二:Bean Validation注解实现 四、实验三:基于方法实现校验 五、实验四:实现自定义校验
60 2
Spring之数据校验:Validation
|
3月前
|
Java Spring
SpringBoot - 优雅的实现【应用启动参数校验】
SpringBoot - 优雅的实现【应用启动参数校验】
56 0
|
4月前
|
前端开发 Java 数据库连接
9:参数校验-Java Spring
9:参数校验-Java Spring
30 0
|
3月前
|
Java 数据安全/隐私保护
SpringBoot - 优雅的实现【参数分组校验】高级进阶
SpringBoot - 优雅的实现【参数分组校验】高级进阶
39 0
|
1月前
|
Java 数据库 数据安全/隐私保护
【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制
【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制
|
1天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
4 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
3天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
28 0
【Spring系列】Sping VS Sping Boot区别与联系
|
4月前
|
Java 数据库连接 Maven
【Spring】掌握 Spring Validation 数据校验
【Spring】掌握 Spring Validation 数据校验
124 0
|
1月前
|
存储 NoSQL 前端开发
【SpringBoot】Redis集中管理Session和自定义用户参数解决登录状态及校验问题
【SpringBoot】Redis集中管理Session和自定义用户参数解决登录状态及校验问题