使用JSR303对数据进行校验【JAVA】
前言
1、SpringBoot项目中Controller的validator做参数校验不生效的问题
解决:
springboot 2.3之前的集成在spring-boot-starter-web
里了,所以不需要额外引入包
springboot 2.3之后需要引入 spring-boot-starter-validation
简单校验
流程:
②在controller方法内,在对应的实体类前面添加 @Valid 注解,开启属性校验功能
代码:
public class UserEntity {@Min(value = 0,message = "年龄不能小于0!") //@Min该注解表示age的值要大于或等于0private Integer age;@NotBlank(message = "用户名不能为空!") //@NotBlank注解表示该属性不能为空private String username;@NotBlank(message = "密码不能为空!")@Length(min = 6,message = "密码不能低于6位!") //@Length该注解表示长度要大于6private String password;private String nickname;
}
/* 简单的校验规则*/
@RestController
public class SimpleValidRule {/* @param user 要校验的实体类* @param result 如果不满足校验规则,可以通过该类接受异常信息* @return* @Valid 开启数据校验功能*/@RequestMapping("/simpleSave")private Map saveUser(@Valid @RequestBody UserEntity user, BindingResult result) {Map<String, String> map = new HashMap<>();if (result.hasErrors()) {result.getFieldErrors().forEach(item -> {//获取失败信息map.put(item.getField(), item.getDefaultMessage());});} else {map.put("data", "数据校验成功");}return map;}}
分组校验
ps:上面通过简单案例演示了数据校验,但是可以看到属性校验返回的异常信息是跟业务代码写在一起,这样看起来非常不优雅,同时也不利于后面维护,所以对于异常信息,我们应该使用一个全局异常处理。
流程:
①还是在需要校验属性的上面添加校验规则,但是这里还添加了一个分组属性。想象一下,如果执行save方法时,我们必须要求用户输入username属性,执行update方法时,必须用户传入age属性,那如果通过上面校验,发现并不能满足这种要求,所以这就是分组的意义。
②在controller方法内,通过注解@Validated开启属性校验,通过指定要校验的规则,如果不指定,则跟上面简单校验一样。
代码:
public class UserEntity2 {//这里可以看到,该属性校验多了一个groups分组属性@Min(value = 0, message = "年龄不能小于0!", groups = UpdateGroup.class) private Integer age;//可以看到,username跟前面的age属性定义了不同组@NotBlank(message = "用户名不能为空!", groups = SaveGroup.class) private String username;@NotBlank(message = "密码不能为空!")@Length(min = 6, message = "密码不能低于6位!")private String password;@NotBlank(message = "用户昵称不能为空", groups = {SaveGroup.class})private String nickname;
}
/* 指定分组校验*/
@RestController
public class GroupValidRule {//这里通过@Validated注解定义了只校验那个分组下的属性,其它分组,包括没写的,不会进行校验@PostMapping("/groupSave")private String saveUser(@Validated(value = SaveGroup.class) @RequestBody UserEntity user) {/* 执行一系列流程... */return "数据校验成功";}@PostMapping("/groupUpdate")private String updateUser(@Validated(value = UpdateGroup.class) @RequestBody UserEntity user) {/* 执行一系列流程... */return "数据校验成功";}
}
/* 全局异常处理类*/
@RestControllerAdvice
public class GlobalException {@ExceptionHandlerpublic String handlerException(Exception e){return "数据校验失败!";}
}
上面如果执行saveUser方法,因为使用注解@Validated(value = SaveGroup.class)定义了只校验SaveGroup分组下的属性,所以只有username属性会被进行校验!
自定义校验
可能以上校验规则并不一定符合我们,所以这时候可以通过自定义校验规则
流程:
①new一个自定义注解类
②创建具体的校验规则类,需要实现ConstraintValidator接口,重写两个方法:initialize()和isValid()
③在需要自定义校验属性上添加该注解,并指定校验规则
代码:
@Data
public class UserEntity3 {//这里自定义校验规则,要求必须输入name的值为张三@CustomValidAnnotation(name = "张三") private String name;
}
/* 自定义校验*/
@RestController
public class CustomValidRule {@RequestMapping("/customSave")private String saveUser(@Validated @RequestBody UserEntity user) {return "属性校验成功";}
}
/* 自定义校验注解*/
@Constraint(validatedBy = {CustomValid.class} //自定义具体的校验器,可以指定多个
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomValidAnnotation {String message() default "";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};String name(); //添加一个name属性作为校验规则}
/* 自定义校验器,需要实现ConstraintValidator并传入两个泛型,第一个是自定义校验的注解,第二个校验属性的类型*/
public class CustomValid implements ConstraintValidator<CustomValidAnnotation, String> {private String name;/* 这个方法可以获取我们自定义校验属性的值,也就是在实体类定义属性校验规则* @param constraintAnnotation 自定义注解*/@Overridepublic void initialize(CustomValidAnnotation constraintAnnotation) {String name = constraintAnnotation.name(); //获取自定义注解上的valuethis.name = name;}/* 这个方法是获取用户传入的数据,我们可以通过上面拿到的校验规则与用户输入进行校验匹配,判断用户输入是否满足我们要求的参数格式* @param s 用户输入的值* @param constraintValidatorContext* @return 返回false表示输入的数据不合法,true则相反*/@Overridepublic boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {if (name.equals(name)){return true;}return false;}
}