引入依赖
如果是使用springboot的话,则不用导入依赖,springboot内置了。如果是springmvc+spring则需要导入依赖。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.5.Final</version>
</dependency>
复制代码
Controller的方法有两种传参方式,一种是直接传参,一种是通过对象传参。
直接传参
@Controller
@Validated
public class IndexController {
@GetMapping("/index")
@ResponseBody
public Object index(@NotBlank(message = "名称不能为空") String name,@NotNull(message = "年龄不能为空") @Range(min = 1,max = 100,message = "年龄必须在1-100之间") Integer age){
System.out.println("name:"+name+"--age:"+age);
return "success";
}
}
复制代码
这种方式,需要注意的是类必须加上注解@Validated
,不然校验会不起作用。
对象传参
定义实体类
public class User implements Serializable {
@NotBlank(message = "名称不能为空")
private String name;
@NotNull(message = "年龄不能为空")
@Range(min = 1,max = 100,message = "年龄必须在1-100之间")
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
复制代码
定义接口,接收对象前需要使用@Validated
修饰,使用@Valid
修饰也可以,不过@Validated
可以使用组来进行区分校验。
@Controller
public class IndexController {
@RequestMapping("/index3")
public Object index3(@RequestBody @Validated User user){
System.out.println("name:"+user.getName()+"--age:"+user.getAge());
return "success";
}
}
复制代码
添加全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
private static String PARAM_FAIL_CODE = "1002";
private static String VALIDATION_CODE = "1003";
Logger logger = LogManager.getLogger(GlobalExceptionHandler.class);
/**
* 方法参数校验
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
logger.error(e.getMessage(), e);
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
for (ObjectError error : allErrors) {
logger.error(error.getDefaultMessage());
}
return new CommonResult(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
}
/**
* ValidationException
*/
@ExceptionHandler(ValidationException.class)
public CommonResult handleValidationException(ValidationException e) {
logger.error(e.getMessage(), e);
return new CommonResult(VALIDATION_CODE, e.getCause().getMessage());
}
/**
* ConstraintViolationException
*/
@ExceptionHandler(ConstraintViolationException.class)
public CommonResult handleConstraintViolationException(ConstraintViolationException e) {
StringBuilder sb = new StringBuilder();
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
for (ConstraintViolation<?> item : violations) {
sb.append(item.getMessage()).append(",");
/**打印验证不通过的信息*/
logger.error(item.getMessage());
}
sb.deleteCharAt(sb.length()-1);
return new CommonResult(PARAM_FAIL_CODE, sb.toString());
}
@ExceptionHandler(BindException.class)
public CommonResult handleBindException(BindException e) {
logger.error(e.getMessage(), e);
List<ObjectError> allErrors = e.getAllErrors();
for (ObjectError error : allErrors) {
logger.error(error.getDefaultMessage());
}
return new CommonResult(VALIDATION_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
}
@ExceptionHandler(NoHandlerFoundException.class)
public CommonResult handlerNoFoundException(Exception e) {
logger.error(e.getMessage(), e);
return new CommonResult("404", "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(Exception.class)
public CommonResult handleException(Exception e) {
logger.error(e.getMessage(), e);
return new CommonResult("500", "系统繁忙,请稍后再试");
}
}
复制代码
设置校验器为快速失败模式
设置校验器为快速失败模式可以在第一个校验失败时直接返回,而不用全部校验完毕在返回。
@Configuration
public class ValidatorConfiguration {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
/**设置validator模式为快速失败返回*/
postProcessor.setValidator(validator());
return postProcessor;
}
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast(true)//快速失败返回模式
// .addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
复制代码
使用组来复用对象校验
定义组
public interface UserSaveGroup extends Default {
}
public interface UserUpdateGroup extends Default {
}
复制代码
修改实体类,添加字段
public class User implements Serializable {
@NotBlank(message = "名称不能为空",groups = UserSaveGroup.class)
private String name;
@NotNull(message = "年龄不能为空")
@Range(min = 1,max = 100,message = "年龄必须在1-100之间")
private Integer age;
@NotBlank(message = "名称不能为空",groups = UserUpdateGroup.class)
private String nickName;
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
复制代码
@Controller
public class IndexController {
@RequestMapping("/index4")
public Object index4(@RequestBody @Validated(UserSaveGroup.class) @Valid User user){
System.out.println("name:"+user.getName()+"--age:"+user.getAge());
return "success";
}
@RequestMapping("/index5")
public Object index5(@RequestBody @Validated(UserUpdateGroup.class) @Valid User user){
System.out.println("name:"+user.getName()+"--age:"+user.getAge());
return "success";
}
}
复制代码
age字段没有使用group修饰,默认是在Default组里。而UserSaveGroup和UserUpdateGroup都继承了Default组,所以/index4
接口会校验name
和age
字段,而/index5
则会校验nickName
和age
字段。
自定义参数注解
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidator.class)
public @interface IdCard {
String message() default "身份证号码不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
复制代码
public class IdCardValidator implements ConstraintValidator<IdCard, Object> {
@Override
public void initialize(IdCard constraintAnnotation) {
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
if(null==o)
return false;
return isIdCard(o.toString());
}
private boolean isIdCard(String str) {
String regex = "(^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{2}[0-9Xx]$)";
return match(regex, str);
}
private static boolean match(String regex, String str) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
return matcher.matches();
}
}
复制代码
常用校验注解
注解 | 类型 | 说明 |
---|---|---|
@NotNull | 任意类型 | 验证注解的元素值不是null |
@Null | 任意类型 | 验证注解的元素值是null |
@Min(value=值) | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型 | 验证注解的元素值大于等于@Min指定的value值 |
@Max(value=值) | 字符串、Collection、Map、数组等 | 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
@Size(min=下限, max=上限) | 和@Min要求一样 | 验证注解的元素值小于等于@Max指定的value值 |
@NotBlank | CharSequence子类型 | 验证注解的元素值不为空(不为null、不为空字符串、trim之后不为空字符串) |
@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 | 任何非原子类型 | 指定递归验证关联的对象如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证 |
参考文章: