SpringBoot参数校验注解
使用自定义参数校验的优势
1.消除耦合,如果哪天你需要更改正则表达式你需要在每个引用的地方进行更改
2.通俗易懂,可以一目了然表示这是一个手机校验的注解,即便代码的阅读者对正则表达式不太熟悉,也可以猜出这个注解用来干嘛的
3.更强大,自定义校验逻辑,可以干更多的事情,甚至可以在校验器中引入其他的组件如使用@Autowired引入服务类进行处理校验判断
maven配置
spring-boot-starter-web包自动依赖hibernate-validator,不用再重复引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.1.Final</version>
</dependency>
声明一个bean注册到spring容器,这个bean是一个容器后处理器,会把校验的逻辑通过AOP织入有@Validated注解的class,具体可以看这个类的源码
这一步在springboot其实也不用做,ValidationAutoConfiguration这个配置类自动帮我们做了
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(){
return new MethodValidationPostProcessor();
}
验证不通过会产生异常,因为我们项目提供rest接口,所以通过全局捕获异常,然后转换为json给前台
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 用来处理bean validation异常
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public WebResult resolveConstraintViolationException(ConstraintViolationException ex){
WebResult errorWebResult = new WebResult(WebResult.FAILED);
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
if(!CollectionUtils.isEmpty(constraintViolations)){
StringBuilder msgBuilder = new StringBuilder();
for(ConstraintViolation constraintViolation :constraintViolations){
msgBuilder.append(constraintViolation.getMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if(errorMessage.length()>1){
errorMessage = errorMessage.substring(0,errorMessage.length()-1);
}
errorWebResult.setInfo(errorMessage);
return errorWebResult;
}
errorWebResult.setInfo(ex.getMessage());
return errorWebResult;
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public WebResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex){
WebResult errorWebResult = new WebResult(WebResult.FAILED);
List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
if(!CollectionUtils.isEmpty(objectErrors)) {
StringBuilder msgBuilder = new StringBuilder();
for (ObjectError objectError : objectErrors) {
msgBuilder.append(objectError.getDefaultMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if (errorMessage.length() > 1) {
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
}
errorWebResult.setInfo(errorMessage);
return errorWebResult;
}
errorWebResult.setInfo(ex.getMessage());
return errorWebResult;
}
}
这两个异常分别对应校验的两种使用方式
这两个异常分别对应校验的两种使用方式
- 在方法里面校验
- 在bean对象里面校验
经过测试,以上两种形式的数据验证不仅仅对controller层有用,在service层也行,只要这个类在spring ioc容器里面
常用的参数校验的注解
注解 | 描述 |
---|---|
@Valid | 对po实体尽心校验 |
@AssertFalse | 所注解的元素必须是Boolean类型,且值为false |
@AssertTrue | 所注解的元素必须是Boolean类型,且值为true |
@DecimalMax | 所注解的元素必须是数字,且值小于等于给定的值 |
@DecimalMin | 所注解的元素必须是数字,且值大于等于给定的值 |
@Digits | 所注解的元素必须是数字,且值必须是指定的位数 |
@Future | 所注解的元素必须是将来某个日期 |
@Max | 所注解的元素必须是数字,且值小于等于给定的值 |
@Min | 所注解的元素必须是数字,且值大于等于给定的值 |
@Range | 所注解的元素需在指定范围区间内 |
@NotNull | 所注解的元素值不能为null |
@NotBlank | 所注解的元素值有内容 |
@Null | 所注解的元素值为null |
@Past | 所注解的元素必须是某个过去的日期 |
@PastOrPresent | 所注解的元素必须是过去某个或现在日期 |
@Pattern | 所注解的元素必须满足给定的正则表达式 |
@Size | 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内 |
所注解的元素需满足Email格式 |
使用方法
在方法参数上使用
@Controller
@Validated
public class ValidationController {
@GetMapping("/validate1")
@ResponseBody
public String validate1(
@Size(min = 1,max = 10,message = "姓名长度必须为1到10")@RequestParam("name") String name,
@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100") @RequestParam("age") Integer age,
@Future @RequestParam("birth")@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss") Date birth){
return "validate1";
}
}
在bean内属性上使用
public class User {
@Size(min = 1,max = 10,message = "姓名长度必须为1到10")
private String name;
@NotEmpty
private String firstName;
@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100")
private Integer age;
@Future
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date birth;
...getter setter
}
在controller对应User实体前增加@Valid注解
@PostMapping("/validate2")
@ResponseBody
public User validate2(@Valid @RequestBody User user){
return user;
}
message支持表达式和EL表达式 ,比如message = “姓名长度限制为{min}到{max} ${1+2}”)
想把错误描述统一写到properties的话,在classpath下面新建ValidationMessages_zh_CN.properties文件(注意value需要转换为unicode编码),然后用{}格式的占位符
扩展
创建约束注解类
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { HandsomeBoyValidator.class})
public @interface HandsomeBoy {
String message() default "盛超杰最帅";
String name();
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
注意:message用于显示错误信息这个字段是必须的,groups和payload也是必须的
@Constraint(validatedBy = { HandsomeBoyValidator.class})用来指定处理这个注解逻辑的类
一开始写了这个自定义注解和验证类,发现没有生效,最后发现是@Constraint这个注解里的类没有配置,还跟了很多源码,蛋疼,总的来讲,这个配置还是挺方便的
创建验证器类
public class HandsomeBoyValidator implements ConstraintValidator<HandsomeBoy, User> {
private String name;
/**
* 用于初始化注解上的值到这个validator
* @param constraintAnnotation
*/
@Override
public void initialize(HandsomeBoy constraintAnnotation) {
name =constraintAnnotation.name();
}
/**
* 具体的校验逻辑
* @param value
* @param context
* @return
*/
@Override
public boolean isValid(User value, ConstraintValidatorContext context) {
return name ==null || name.equals(value.getName());
}
}
这边的功能是user类里面的name字段必须和配置的一样,否则输出一个事实
@PostMapping("/validate3")
@ResponseBody
public User validate3(@Valid @HandsomeBoy(name = "scj",message = "盛超杰第二帅") @RequestBody User user){
return user;
}
demo源码下载
https://github.com/shengchaojie/springboot-validation-demo
本人有所修改,与原文有所差别。
链接:https://www.jianshu.com/p/89a675b7c900
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。