SpringMVC之服务端数据校验
B/S 系统中对 http 请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的,实际上,几乎所有的系统,凡是涉及到数据校验,都需要在服务端进行二次校验。为什么要在服务端进行二次校验呢?这需要理解客户端校验和服务端校验各自的目的。
(1)、客户端校验,我们主要是为了提高用户体验,例如用户输入一个邮箱地址,要校验这个邮箱地址是否合法,没有必要发送到服务端进行校验,直接在前端用 js 进行校验即可。但是大家需要明白的是,前端校验无法代替后端校验,前端校验可以有效的提高用户体验,但是无法确保数据完整性,因为在 B/S 架构中,用户可以方便的拿到请求地址,然后直接发送请求,传递非法参数。
(2)、服务端校验,虽然用户体验不好,但是可以有效的保证数据安全与完整性。
(3)、综上,实际项目中,两个一起用。
Spring 支持 JSR-303 验证框架,JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是 Hibernate Validator(与Hibernate ORM 没有关系),JSR-303 用于对 Java Bean 中的字段的值进行验证。
1、先来使用maven导入相关jar包,此版本需要jdk8+:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
2、接下来,在 SpringMVC 的配置文件中配置校验器:
<!--验证器-->
<bean id="myValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>
<!--配置注解驱动-->
<mvc:annotation-driven validator="myValidator"/>
配置时,提供一个 LocalValidatorFactoryBean 的实例,然后 Bean 的校验使用 HibernateValidator。
在这里需要提交的数据中,假设学生姓名不能为空,学生姓名长度在4-20之间,手机号要合法,年龄不能超过 120。那么在定义实体类的时候,就可以加入这个判断条件了。
3、定义User
public class User {
@NotEmpty
@Size(min = 4, max = 20, message = "姓名长度必须在{min}到{max}之间")
private String name;
@Min(value = 0, message = "年龄不能小于{value}岁")
@Max(value = 120, message = "年龄不能大于{value}岁")
private int age;
@Pattern(regexp = "^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$", message = "手机号码不正确")
private String phone;
get/set方法
......
在这里:
@NotEmpty :表示这个字段不能为空
@Size :中描述了这个字符串长度的限制
@Pattern :表示匹配一个正则表达式
@Max :表示这个字段的最大值
另外,出错信息也可以在properties文件中配置,具体参考文档:
http://springmvc.javaboy.org/2019/1112/validation#10-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C
4、定义完成后,接下来,在 Controller 中定义接口:
@Controller
public class ValidateController {
@RequestMapping("/validate")
//不能将@Validated 注解在String类型和基本类型的形参前。
//BindingResult参数可以获取到所有验证异常的信息
public ModelAndView testValidation(@Validated User user, BindingResult br) {
System.out.println(user);
ModelAndView mv = new ModelAndView();
List<ObjectError> allErrors = br.getAllErrors();
if (allErrors != null && allErrors.size() > 0) {
FieldError nameError = br.getFieldError("name");
FieldError ageError = br.getFieldError("age");
FieldError phoneError = br.getFieldError("phone");
if (nameError != null) {
mv.addObject("nameError", nameError.getDefaultMessage());
}
if (ageError != null) {
mv.addObject("ageError", ageError.getDefaultMessage());
}
if (phoneError != null) {
mv.addObject("phoneError", phoneError.getDefaultMessage());
}
//若校验不通过,则回显
mv.setViewName("/login.jsp");
return mv;
}
//校验通过
mv.addObject("name", user.getName());
mv.setViewName("user");
return mv;
}
}
在这里:
@Validated 表示 Student 中定义的校验规则将会生效
BindingResult 表示出错信息,如果这个变量不为空,表示有错误,否则校验通过。
接下来就可以启动项目了。访问 jsp 页面,然后添加User,查看校验规则是否生效。
5、接下来,我们提供一个添加用户的页面,获取校验信息:
<form method="post" action="validate">
姓名:<input type="text" name="name">${nameError}<br>
年龄:<input type="text" name="age">${ageError}<br>
电话:<input type="text" name="phone">${phoneError}<br>
<input type="submit" value="提交"><br>
</form>
6、常用的校验注解
- @AssertFalse 验证注解的元素值是 false
- @AssertTrue 验证注解的元素值是 true
- @DecimalMax(value=x) 验证注解的元素值小于等于指定的十进制value 值
- @DecimalMin(value=x) 验证注解的元素值大于等于指定的十进制value 值
- @Digits(integer=整数位数, fraction=小数位数)验证注解的元素值的整数位数和小数位数上限
- @Future 验证注解的元素值(日期类型)比当前时间晚
- @Max(value=x) 验证注解的元素值小于等于指定的 value值
- @Min(value=x) 验证注解的元素值大于等于指定的 value值
- @NotNull 验证注解的元素值不是 null
- @Null 验证注解的元素值是 null
- @Past 验证注解的元素值(日期类型)比当前时间早
- @Pattern(regex=正则表达式) 验证注解的元素值不指定的正则表达式匹配
- @Size(min=最小值, max=最大值) 验证注解的元素值的在 min 和 max (包含)指定区间之内,如字符长- 度、集合大小
- @Valid 该注解主要用于字段为一个包含其他对象的集合或map或数组的字段,或该字段直接为一个其他对象的引用,这样在检查当前对象的同时也会检查该字段所引用的对象。
- @NotEmpty 验证注解的元素值不为 null 且不为空(字符串长度不为 0、集合大小不为 0)
- @Range(min=最小值, max=最大值)验证注解的元素值在最小值和最大值之间
- @NotBlank 验证注解的元素值不为空(不为 null、去
- 除首位空格后长度为 0),不同于@NotEmpty, @NotBlank 只应用于字符串且在比较时会去除字符串的空格
- @Length(min=下限, max=上限) 验证注解的元素值长度在 min 和 max 区间内
- @Email 验证注解的元素值是 Email,也可以通过正则表达式和 flag 指定自定义的 email 格式