曾经参数的验证是这样的:java
public String test(User user){ if(user == null){ throw new NullPointerException("user 不能为空"); } if(user.getUserName() == null){ throw new NullPointerException("userName 不能为空"); } if(user.getUserName().length() < 4 || user.getUserName().length()>10){ throw new RuntimeException("userName 长度小于4位或大于10位"); } return "success"; }
随着参数的增长,格式的变化,校验数据有效性的代码愈发繁琐。git
经过Spring boot来完成参数数据校验。web
JSR-303注解介绍正则表达式
这里只列举了javax.validation包下的注解,同理在spring-boot-starter-web包种也存在hibernate-validator验证包,里面包含了一些javax.validation没有的注解。spring
注解 | 说明 |
---|---|
@NotNull |
限制必须不为null |
@NotEmpty |
验证注解的元素值不为 null 且不为空(字符串长度不为0、集合大小不为0) |
@NotBlank |
验证注解的元素值不为空(不为null、去除首位空格后长度为0),不一样于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格 |
@Pattern(value) |
限制必须符合指定的正则表达式 |
@Size(max,min) |
限制字符长度必须在 min 到 max 之间(也能够用在集合上) |
@Email |
验证注解的元素值是Email,也能够经过正则表达式和flag指定自定义的email格式 |
@Max(value) |
限制必须为一个不大于指定值的数字 |
@Min(value) |
限制必须为一个不小于指定值的数字 |
@DecimalMax(value) |
限制必须为一个不大于指定值的数字 |
@DecimalMin(value) |
限制必须为一个不小于指定值的数字 |
@Null |
限制只能为null(不多用) |
@AssertFalse |
限制必须为false (不多用) |
@AssertTrue |
限制必须为true (不多用) |
@Past |
限制必须是一个过去的日期 |
@Future |
限制必须是一个未来的日期 |
@Digits(integer,fraction) |
限制必须为一个小数,且整数部分的位数不能超过 integer,小数部分的位数不能超过 fraction (不多用) |
实体类浏览器
public class Book implements Serializable { private Integer id; @NotBlank(message = "name 不能为空") @Length(min = 2, max = 10, message = "name 长度必须在{min}-{max}之间") private String name; @NotNull(message = "price 不能为空") @DecimalMin(value = "0.1", message = "价格不能低于 {value}") private BigDecimal price; ... }
控制层app
这些验证注解不单单能够放在controller上,也能够加在service层上。ide
//todo 开启数据有效性校验,添加在类上即为验证方法,添加在方法参数中即为验证参数对象。(添加在方法上无效) @Validated @RequestMapping("/books")
@RestController public class BookController { @GetMapping("/test1") public String test1(@NotBlank(message = "name不能为空") @Length(min = 2, max = 10 , message="name 长度必须在{min}-{max}之间") String name){ return "test1"; } @GetMapping("test2") public String test2(@Validated Book book){ return "test2"; } }
这样就能够了,在浏览器中输入 /books/test1/ 不输入参数会抛异常 /books/test1?name=sdf 则能够经过验证spring-boot
/books/test2/ 异常 /books/test2?name=sdf&price=0.5就ok了测试
Spring Boot 还容许咱们自定义Validatior
自定义注解
package com.spring.boot.utils; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //@Target定义范围 在字段 参数上使用 @Target({ElementType.FIELD, ElementType.PARAMETER}) //定义可见范围 RUNTIME整个运行阶段均可见 @Retention(RetentionPolicy.RUNTIME) //Constraint指定具体校验器类 @Constraint(validatedBy = DateTimeValidator.class) //@interface 定义注解 public @interface DateTime { String message() default "格式错误"; String format() default "yyyy-MM-dd"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
具体的校验器
package com.spring.boot.utils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.text.ParseException; import java.text.SimpleDateFormat; /** * 日期格式验证 */ public class DateTimeValidator implements ConstraintValidator<DateTime, String> { private DateTime dateTime; @Override //初始化,它能够得到当前注解的全部属性 public void initialize(DateTime constraintAnnotation) { this.dateTime = constraintAnnotation; } @Override //约束验证的主体方法,其中s就是验证参数的具体事例,context表明约束执行的上下文 public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { //若是为空不用验证, 为空验证能够用@NotBlank、@NotNull等参数进行控制 if (s == null) { return true; } String format = dateTime.format(); if (s.length() != format.length()) { return false; } SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); try { simpleDateFormat.parse(s); } catch (ParseException e) { return false; } return true; } }
而后在控制层使用他
@GetMapping("/test3") public String test3(@NotBlank(message = "date 不能为空") @DateTime(message = "您如的格式错误,正确的格式为:{format}",format = "yyyy-MM-dd HH:mm") String date){ return "test3"; }
测试
分组验证
有时候咱们须要对一个实体类有多种验证方式,在不一样的状况下使用不一样的验证方式,好比id,新增的时候是不须要的,更新时是必须的。
定义一个验证组,里面写上不一样的空接口类便可
public class Groups { public interface Update{ } public interface Default{ } }
实体类
groups属性的做用就让@Validated注解只验证与自身value属性想匹配的字段,可多个,只要知足就会去归入验证范围。
public class Book { @NotNull(message = "id 不能为空" , groups = Groups.Update.class) private Integer id; @NotBlank(message = "name 不能为空" , groups = Groups.Default.class) private String name; @NotNull(message = "price 不能为空" , groups = Groups.Default.class) private BigDecimal price;
控制层
建立一个ValidateControoler类,而后定义好insert、update两个方法,因为insert方法并不关心id字段,因此这里的@Validate的value属性写成Groups.Default.class就能够了,而update方法须要Id,因此此处@Validated注解的value属性值就要写成Groups.Default.class,Groups.Update.Class。表明只要是这分组下的数据都须要进行数据有效性校验操做
@RestController public class ValidateController { //default中没有id @GetMapping("/insert") public String insert(@Validated(value=Groups.Default.class) Book book){ return "insert"; } //update中有id校验 @GetMapping("update") public String update(@Validated(value={Groups.Default.class,Groups.Update.class}) Book book){ return "update"; } }