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的规则。

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

示例仓库地址

相关文章
|
6月前
|
前端开发 Java 数据库连接
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
|
12月前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
2785 17
Spring Boot 两种部署到服务器的方式
|
10月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
399 0
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
599 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
811 2
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
542 2
|
Java Spring
springBoot 使用 @NotEmpty,@NotBlank,@NotNull 及@Valid注解校验请求参数
springBoot 使用 @NotEmpty,@NotBlank,@NotNull 及@Valid注解校验请求参数
1061 7
springboot自定义拦截器,校验token
springboot自定义拦截器,校验token
940 6
|
缓存 NoSQL Java
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
298 5
|
Java Maven
springboot项目打jar包后,如何部署到服务器
springboot项目打jar包后,如何部署到服务器
1131 1