本章概要
13.5 数据校验
数据校验时开发过程中一个常见的环节,一般来说,为了提高系统运行效率,都会在前端进行数据校验,但是这并不意味着不必在后端做数据校验了,因为用户还是可能在获取数据接口后手动传入非法数据。
13.5.1 普通校验
普通校验时基础用法,首先在 Spring Boot Web 项目中添加 数据校验相关的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
项目创建成功后,查看 LocalValidatorFactoryBean 类的源码,发现默认的 ValidationMessageSource (校验出错时的提示文件)是 resources 目录下的 ValidationMessage.properties 文件,创建该文件。
user.name.size=用户名长度介于5到10个字符之间
user.address.notnull=用户地址不能为空
user.age.size=年龄输入不正确
user.email.notnull=邮箱不能为空
user.email.pattern=邮箱格式不正确
创建 User 类,配置数据校验
public class User {
private Integer id;
@Size(min = 5,max = 10,message = "{user.name.size}")
private String name;
@NotNull(message = "{user.address.notnull}")
private String address;
@DecimalMin(value = "1",message = "{user.age.size}")
@DecimalMax(value = "200",message = "{user.age.size}")
private Integer age;
@Email(message = "{user.email.pattern}")
@NotNull(message = "{user.email.notnull}")
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
代码解释:
- @Size 表示一个字符串的长度或者一个集合的大小,必须在某一个范围中;min 参数表示范围的下限;max 参数表示范围的上线;message 表示校验失败时的提示信息
- @NotNull 注解表示该字段不能为空
- @DecimalMin 注解表示对应属性值的下限,@DecimalMax 注解表示对应属性值的上限
- @Email 注解表示对应属性格式是一个 Email
接着创建 Controller
@RestController
public class UserController {
@PostMapping("/user")
public List<String> addUser(@Validated User user, BindingResult result){
List<String> errors = new ArrayList<>();
if (result.hasErrors()){
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError error : allErrors){
errors.add(error.getDefaultMessage());
}
}
return errors;
}
}
代码解释:
- 给 User 参数添加 @Validated 注解,表示需要对该参数做校验,紧接着的 BindingResult 参数表示在校验出错时保存的出错信息
- 如果 BindingResult 中的 hasErrors 方法返回 true ,表示有错误信息,此时遍历错误信息,将其返回给前端
启动项目,使用 Postman 直接访问 “http://localhost:8080/user”接口
出现乱码情况,添加配置类,覆盖其中的 getValidator 方法
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected Validator getValidator() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setDefaultEncoding("utf-8");
messageSource.setCacheMillis(-1);
messageSource.setBasename("ValidationMessages");
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
factoryBean.setValidationMessageSource(messageSource);
return factoryBean;
}
}
重启项目,再次访问
如果传入用户地址、一个非法邮箱地址、一个格式不正确的用户名,结果如下
13.5.2 分组校验
有的时候开发者在某一个实体类中定义了很多校验规则,但是在某一次业务处理中,并不需要这么多校验规则,此时就可以使用分组校验。 首先创建两个分组接口
public interface ValidationGroup1 {
}
public interface ValidationGroup2 {
}
然后在实体类中添加分组信息
public class User {
private Integer id;
@Size(min = 5,max = 10,message = "{user.name.size}",groups = ValidationGroup1.class)
private String name;
@NotNull(message = "{user.address.notnull}",groups = ValidationGroup2.class)
private String address;
@DecimalMin(value = "1",message = "{user.age.size}")
@DecimalMax(value = "200",message = "{user.age.size}")
private Integer age;
@Email(message = "{user.email.pattern}")
@NotNull(message = "{user.email.notnull}",groups = {ValidationGroup1.class,ValidationGroup2.class})
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
这次在部分注解中添加了 groups 属性,表示该校验规则所属的分组,接下来在 @Validated 注解中指定校验分组
@RestController
public class UserController {
@PostMapping("/user")
public List<String> addUser(@Validated(ValidationGroup2.class) User user, BindingResult result){
List<String> errors = new ArrayList<>();
if (result.hasErrors()){
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError error : allErrors){
errors.add(error.getDefaultMessage());
}
}
return errors;
}
}
@Validated(ValidationGroup2.class 表示这里的校验使用 ValidationGroup2 分组的校验规则,即只校验邮箱地址是否为空、用户地址是否为空。 输入错误的邮箱地址、错误的用户名,测试结果如下
13.5.3 校验注解
实际上的校验注解布置前面提到的几个,完整的校验注解如下
- @NotNull 值不能为空
- @Null 值必须为空
- @Pattern(regex=) 字符串必须匹配正则表达式
- @Size 集合或者数组元素的数量必须在min和max之间
- @CreditCardNumber(ignoreNonDigitCharacters=) 字符串必须是信用卡号,按照美国的标准验证
- @Email 字符串必须是Email格式
- @Length(min,max) 校验字符串的长度
- @NotBlank 字符串不能为空串
- @NotEmpty 字符串不能为null,集合或者数组的size不能为空
- @Range(min,max) 数字必须大于min,小于max
- @SafeHtml 字符串必须是安全的html
- @URL 字符串必须是合法的URL地址
- @AssertFalse 值必须是false
- @AssertTrue 值必须是true
- @DecimalMax(value=,inclusive=) ( inclusive=true 小于等于 / inclusive =false 小于)值必须小于等于/小于指定的值,也可以注解在字符串类型的属性上
- @DecimalMin(value=,inclusive=) ( inclusive=true 大于等于 / inclusive =false 大于)值必须大于等于/大于指定的值,也可以注解在字符串类型的属性上
- @Digist(integer=) 数字格式检查。integer指定整数部分的最大长度,fraction指定小数部分的最大长度
- @Future 时间必须是未来的
- @Past 事件必须是过去的
- @Max(vaue=) 值必须小于等于value指定的值,不能注解在字符串类型的属性上
- @Min(value=) 值必须大于等于value指定的值,不能注解在字符串类型的属性上
- @PositiveOrZero 值必须为正整数
|