服务器端的数据验证,对于一个WEB应用来说是非常重要的。现在前后端分离的请求来说,一般是前端验证后,直接调用后端接口,将数据传入后端处理。如果直接调用接口,那么数据就没有通过验证,这是危险的或者说后端得到的数据是不可控的。所以后端验证是必要的。
Spring Boot中引入验证依赖
<!-- Spring boot添加验证支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Spring中提供了hibernate-validator的适配,其实最终就是用的hibernate-validator。
验证类
Spring 支持Hibernate Validator提供的各类验证注解。验证类的编写如下
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class UserDTO {
@NotNull
private String name;
@NotNull
@Min(message = "年龄不能小于{value}岁",value = 10)
@Max(message = "{message.ValidTestBean.age.max}",value = 150)
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
message属性可以不用设置,在不设置的情况下,其将使用默认的消息。(查看@NotNull的源码你可以发现其配置的默认消息配置)
message属性中可以使用{value}来引用value属性的值,spring在返回信息的时候会自动替换
message属性可以使用国际化配置属性,如我们示例中的{message.ValidTestBean.age.max}。当然要使用我们自定义的国际化配置文件需要一些设置。这个我们后面再说。
DTO:data Transfer Object,数据传输对象,可以认为就是前端参数在后端的表示。
Controller参数验证代码
import com.yyoo.springmvc.bean.dto.UserDTO;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("validate")
public class Demo3Controller {
@RequestMapping("test1")
public void test1(@RequestBody @Valid UserDTO bean, BindingResult errors){
System.out.println(errors.hasErrors()+"======hasErrors");
System.out.println(errors.hasFieldErrors()+"=====fieldErrors");
for(FieldError error : errors.getFieldErrors()){
System.out.println("验证的字段名称:"+error.getField());
System.out.println("验证规则名称:" + error.getCode());
System.out.println("提示信息:" + error.getDefaultMessage());
System.out.println("参数名称:" + error.getObjectName());
for(Object obj : error.getArguments()){
System.out.println("=======" + obj);
}
for(String str : error.getCodes()){
System.out.println("+++++++"+str);
}
}
}
}
注意:BindingResult 参数必须紧跟我们要验证的参数,中间不能有其他参数隔开,否则BindingResult 对象无法获得验证信息。
示例中我们只是遍历了对应的错误认证信息,实际使用中我们需要把我们需要的信息返回给前端处理显示。
国际化支持
示例中@NotNull,的提示信息为:“不能为null”,这个默认的提示信息是在hibernate-validator-x.x.x.Final.jar包中定义的,国际化配置文件的名称为:ValidationMessages,那么我们是中文环境使用的默认国际化配置为ValidationMessages_zh_CN.properties。其定义如下:
javax.validation.constraints.AssertFalse.message = 只能为false
javax.validation.constraints.AssertTrue.message = 只能为true
javax.validation.constraints.DecimalMax.message = 必须小于${inclusive == true ? '或等于' : ''}{value}
javax.validation.constraints.DecimalMin.message = 必须大于${inclusive == true ? '或等于' : ''}{value}
javax.validation.constraints.Digits.message = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
javax.validation.constraints.Email.message = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message = 最大不能超过{value}
javax.validation.constraints.Min.message = 最小不能小于{value}
javax.validation.constraints.Negative.message = 必须是负数
javax.validation.constraints.NegativeOrZero.message = 必须是负数或零
javax.validation.constraints.NotBlank.message = 不能为空
javax.validation.constraints.NotEmpty.message = 不能为空
javax.validation.constraints.NotNull.message = 不能为null
javax.validation.constraints.Null.message = 必须为null
javax.validation.constraints.Past.message = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message = 需要匹配正则表达式"{regexp}"
javax.validation.constraints.Positive.message = 必须是正数
javax.validation.constraints.PositiveOrZero.message = 必须是正数或零
javax.validation.constraints.Size.message = 个数必须在{min}和{max}之间
org.hibernate.validator.constraints.CreditCardNumber.message = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}的校验码不合法, {modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message = 不能为空
org.hibernate.validator.constraints.NotEmpty.message = 不能为空
org.hibernate.validator.constraints.ParametersScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.Range.message = 需要在{min}和{max}之间
org.hibernate.validator.constraints.ScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.URL.message = 需要是一个合法的URL
org.hibernate.validator.constraints.time.DurationMax.message = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
自定义国际化文件
简单的方式实现自定义国际化的方式是,拷贝ValidationMessages对应的properties文件,不要修改文件名称,然后在配置文件中添加自己的配置即可。但如果我们要自定义国际化文件的名称,在配置类中如下配置:
@Bean
public static LocalValidatorFactoryBean getLocalValidatorFactoryBean() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
// 如果你需要在其他地方用到该messageSource,可以将它在容器中声明
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("message");// i18n文件的名称
messageSource.setDefaultLocale(Locale.CHINA);// 默认语言,如果不设置则为当前系统的语言(建议设置)
factoryBean.setValidationMessageSource(messageSource);
return factoryBean;
}
LocalValidatorFactoryBean 是提供验证相关配置的基础类。
Hibernate Validate下的验证注解
验证注解 | 验证的数据类型 | 说明 |
---|---|---|
@AssertFalse | Boolean,boolean | 验证注解的元素值是false |
@AssertTrue | Boolean,boolean | 验证注解的元素值是true |
@NotNull | 任意类型 | 验证注解的元素值不是null |
@Null | 任意类型 | 验证注解的元素值是null |
@Min(value=值) | BigDecimal,BigInteger, byte, | short, int, long,等任何Number或CharSequence(存储的是数字)子类型验证注解的元素值大于等于@Min指定的value值 |
@Max(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@Max指定的value值 |
@DecimalMin(value=值) | 和@Min要求一样 | 验证注解的元素值大于等于@ DecimalMin指定的value值 |
@DecimalMax(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@ DecimalMax指定的value值 |
@Digits(integer=整数位数, fraction=小数位数) | 和@Min要求一样 | 验证注解的元素值的整数位数和小数位数上限 |
@Size(min=下限, max=上限) | 字符串、Collection、Map、数组等 | 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
@Past | java.util.Date,java.util.Calendar;Joda Time类库的日期类型 | 验证注解的元素值(日期类型)比当前时间早 |
@Future | 与@Past要求一样 | 验证注解的元素值(日期类型)比当前时间晚 |
@NotBlank | CharSequence子类型 | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格 |
@Length(min=下限, max=上限) | CharSequence子类型 | 验证注解的元素值长度在min和max区间内 |
@NotEmpty | CharSequence子类型、Collection、Map、数组 | 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) |
@Range(min=最小值, max=最大值) | BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型 | 验证注解的元素值在最小值和最大值之间 |
@Email(regexp=正则表达式,flag=标志的模式) | CharSequence子类型(如String) | 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式 |
@Pattern(regexp=正则表达式,flag=标志的模式) | String,任何CharSequence的子类型 | 验证注解的元素值与指定的正则表达式匹配 |
@Valid | 任何非原子类型,指定递归验证关联的对象; | 作用在接收参数上,表示该参数需要验证。在参数对象中包含其他对象,可以使用该注解表示需要级联验证。这个我们下一篇文章来详细讲解。 |