这里写目录标题
1. 简介
Java项目传参时,可能有许多字段都需要进行校验,可以通过javax.validation.constraints.*包下的注解,再配合 @Valid 和 BindingResult 进行错误信息的返回。
2. 示例代码
Validator可以非常方便的制定校验规则,并自动帮你完成校验。首先在入参里需要校验的字段加上注解,每个注解对应不同的校验规则,并可制定校验失败后的信息:
import javax.validation.constraints.*;
/**
* 用户实体类
*/
public class UserBean {
@NotNull(message = "用户id不能为空")
private String id;
@NotNull(message = "用户名称不能为空")
@Size(max = 50, message = "用户名称最大支持50个字符")
private String name;
@Max(value = 100, message = "年龄不能大于100")
@Min(value = 0, message = "年龄不能小于0")
private int age;
@Max(value = 1, message = "性别只能为0/1")
@Min(value = 0, message = "性别只能为0/1")
private String sex;
@Email(regexp = ".*@qq.com", message = "邮箱格式不正确")
private String email;
@Size(max = 11, message = "电话号码最大支持11个字符")
private String phone;
public UserBean() {
}
public UserBean(String id, String name, int age, String sex, String email, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.email = email;
this.phone = phone;
}
// getter、setter
@Override
public String toString() {
return "UserBean{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
校验规则和错误提示信息配置完毕后,接下来只需要在接口需要校验的参数上加上 @Valid 注解,并添加 BindingResult 参数。Controller层通过对标注有@Valid的JavaBean进行校验,并将校验结果封装在BindingResult 中。
注意:@Valid 和 BindingResult 是一一对应的,如果有多个@Valid,那么每个@Valid后面都需要跟一个 BindingResult 用于接收 JavaBean 的校验信息。
@PostMapping("testValid.do")
public String testValid(@RequestBody @Valid UserBean user, BindingResult bindingResult) {
// 如果有参数校验失败,会将错误信息封装成对象组装在BindingResult里
for (ObjectError error : bindingResult.getAllErrors()) {
return error.getDefaultMessage();
}
return "success";
}
3. 常用校验注解
- @Null 被注释的元素必须是null
- @NotNull 被注释的元素不能为null
- @AssertFalse 被注释的元素必须为false
- @AssertTrue 被注释的元素必须是true
(字符串/数组/集合)校验: - @Pattern(regexp=“reg”) 被注释的元素必须符合给定的正则表达式
- @Size(max, min) 被注释的元素的长度必须在范围内
- @NotEmpty 被注释的元素不为空或者null
- @NotBlank 被注释的元素不为null或者trim()后不为空
- @Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
数值校验: - @Max(value) 被注释的元素必须是一个数字,并且小于或者等于指定的int值
- @Min(value) 被注释的元素必须是一个数字,并且大于或等于指定的int值
- @DecimalMax(value) 被注释的元素必须是一个数字,并且小于或者等于指定的值
- @DecimalMin(value) 被注释的元素必须是一个数字,并且大于或等于指定的值
- @Digits(integer, fraction) 被注释的元素必须是一个数字, 精整数度小于等于integer;小数部分精度小于fraction
- @Range(min=long1, max=long2) 被注释的元素必须是一个数字,其值必须>=long1,并且<=long2
- @Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
日期校验:Date/Calendar - @Post 被注释的元素必须是一个过去的日期
- @Future 被注释的元素必须是一个未来的日期
4. 使用AOP(@Aspect)或@ControllerAdvice进一步封装
在实际开发中,还会进行更深层次的封装,因为这样代码编写很麻烦,并且重复代码很多。
4.1 @ControllerAdvice封装
关于 @ControllerAdvice 的用法可以参考《SpringMVC 中 @ControllerAdvice 注解》。
Validator + 自动抛出异常
@PostMapping("testValid.do")
public String testValid(@RequestBody @Valid UserBean user) {
return "success";
}
当传递一个不符合校验规则的参数给接口,程序会引发 org.springframework.web.bind.MethodArgumentNotValidException 异常。这时候,就是ControllerAdvice发挥作用的时候了。
@ControllerAdvice
public class TestControllerAdvice {
@ExceptionHandler({MethodArgumentNotValidException.class})
public ModelAndView customException(MethodArgumentNotValidException e) {
ModelAndView mv = new ModelAndView();
// 获取参数校验失败信息
List<ObjectError> list = e.getBindingResult().getAllErrors();
StringBuffer errorMsg = new StringBuffer();
for (ObjectError error : list){
errorMsg.append(error.getDefaultMessage() + "\n");
}
mv.addObject("description", errorMsg.toString());
// 返回一个错误页面
mv.setViewName("error");
return mv;
}
}
上面代码会自动处理程序的 MethodArgumentNotValidException 异常,给出友好的代码提示。
4.2 AOP(@Aspect)封装
关于 @Aspect 的用法可以参考《Spring AOP配置 之 @Aspect》。
controller代码:
@PostMapping("testValid.do")
public String testValid(@Valid UserBean user, BindingResult bindingResult) {
return "success";
}
AOP类:
@Aspect
@Component
public class ValidAOP {
// 定义切点
private final String POINT_CUT = "execution( * com.web..controller.*.*(..))";
@Pointcut(value = POINT_CUT)
public void pointCut() {
}
@Around(value = "pointCut() && args(.., bindingResult)")
public Object around(ProceedingJoinPoint proceedingJoinPoint, BindingResult bindingResult) throws Throwable {
Object obj = null;
if (bindingResult.hasErrors()) {
List<ObjectError> errors = bindingResult.getAllErrors();
StringBuffer sb = new StringBuffer();
for (ObjectError error : errors) {
sb.append(error.getDefaultMessage() + "\n");
}
obj = sb.toString();
} else {
obj = proceedingJoinPoint.proceed();
}
return obj;
}
}