1.引言
参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。
那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的,本篇文章主要是介绍 JSR303,Hibernate Validator 等校验工具的使用,以及自定义校验注解的使用。
2. 校验框架介绍
JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。注解如下:
@NotNull 注解元素必须是非空
@Null 注解元素必须是空
@Digits 验证数字构成是否合法
@Future 验证是否在当前系统时间之后
@Past 验证是否在当前系统时间之前
@Max 验证值是否小于等于最大指定整数值
@Min 验证值是否大于等于最小指定整数值
@Pattern 验证字符串是否匹配指定的正则表达式
@Size 验证元素大小是否在指定范围内
@DecimalMax 验证值是否小于等于最大指定小数值
@DecimalMin 验证值是否大于等于最小指定小数值
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
Hibernate validator 在JSR303的基础上对校验注解进行了扩展,扩展注解如下:
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内
3.代码实现
3.1添加JAR包依赖
在pom.xml中添加如下依赖:
<!--jsr 303-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
3.2最简单的参数校验
3.2.1、Model 中添加校验注解
public class Book {
private long id;
@NotEmpty(message = "书名不能为空")
private String bookName;
@NotNull(message = "ISBN号不能为空")
private String bookIsbn;
@DecimalMin(value = "0.1",message = "单价最低为0.1")
private doubleprice; // getter setter ....... }
3.2.2、在controller中使用此校验
@RequestMapping(value = "/book",method = RequestMethod.POST)
public void addBook(@RequestBody @Valid Book book) {
System.out.println(book.toString());
}
当访问这个post接口时,如果参数不符合Model中定义的话,程序中就回抛出400异常,并提示错误信息。
3.3分组验证
对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下
3.3.1、定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则
*/
public interface PersonAddView {
}
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
*/
public interface PersonModifyView {
}
3.3.2、Model上添加注解时使用指明所述的分组
public class Person {
private long id;
/**
* 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
*/
@NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message= "添加、修改用户时名字不能为空",payload = ValidateErrorLevel.Info.class)
@ListNotHasNull.List({
@ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),
@ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})
private String name;
@NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
private String address;
@Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")
@Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
private int age;
//getter setter 方法......
}
3.3.3、启用校验
此时启用校验和之前的不同,需要指明启用哪一组规则
/**
* 添加一个Person对象
* 此处启用PersonAddView这个验证规则
* 备注:此处@Validated(PersonAddView.class)表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,
* 若两个规则同时加上去,则只有第一套起作用
*/
@RequestMapping(value = "/person", method =RequestMethod.POST)
public void addPerson(@RequestBody @Validated({PersonAddView.class,Default.class}) Person person) {
System.out.println(person.toString());
}
/**
* 修改Person对象
* 此处启用PersonModifyView这个验证规则
*/
@RequestMapping(value = "/person", method = RequestMethod.PUT)
public void modifyPerson(@RequestBody @Validated(value ={PersonModifyView.class}) Person person) {
System.out.println(person.toString());
}
3.4 Spring validator 方法级别的校验
JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,spring 在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验,实现如下:
3.4.1、实例化MethodValidationPostProcessor
@Bean
public MethodValidationPostProcessormethodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
3.4.2、在所要实现方法参数校验的类上面添加@Validated,如下
@RestController
@Validated
public class ValidateController {
}
3.4.3、在方法上面添加校验规则:
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String paramCheck(@Length(min = 10) @RequestParam String name) {
System.out.println(name);
return null;
}
3.5使用BindingResult对象来保存验证结果
每一个模型对象后边都需要跟一个Errors或BindingResult对象来保存验证结果,其方法体内部可以使用这两个验证结果对象来选择出错时跳转的页面或处理的逻辑。
@RequestMapping("/validate/multi")
public String multi(@Valid@ModelAttribute("a") A a, BindingResult aErrors, @Valid@ModelAttribute("b") B b, BindingResult bErrors) {
if (aErrors.hasErrors()) { //如果a模型对象验证失败
return "validate/error";
}
if (bErrors.hasErrors()) { //如果a模型对象验证失败
return "validate/error";
}
return "redirect:/success";
}