SpringBoot中提供了可以给实体类上的参数加入校验,对于前端请求的数据进行校验。比如数据的长度、格式、类型、是否为空等等,如果没有通过校验直接报错,大大的减少了在代码中使用if…else进行判断以及防止脏数据对数据库的影响。
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) 验证注解的元素值长度在min和max区间内
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) 验证注解的元素值在最小值和最大值之间
@Range(min=10000,max=50000,message="range.bean.wage")
private BigDecimal wage;
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
@Constraint : 指定自定义注解逻辑类,使用的是反射机制 Class.class
使用这个验证直接的时候,需要在 pom.xml 中加入依赖
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
@Validated和@Valid的区别
- 所属的包不同:@Valid属于javax.validation包下,是jdk给提供的。
@Validated是org.springframework.validation.annotation包下的,是spring提供的。 - @Validated要比@Valid更加强大
- @Validated在@Valid之上提供了分组功能和验证排序功能
使用方法
构建实体类
@Data public class PersonBean implements Serializable { private static final long serialVersionUID = -8374325179529529802L; /** * 年龄 */ @Range(min = 1, max = 99, message = "年龄必须在1~99之间") private Integer personAge; /** * 姓名 */ @Length(min = 5, max = 10, message = "用户名长度必须在5~10之间") private String personName; /** * 密码 */ @Length(min = 5, max = 10, message = "密码长度必须在5~10之间") @NotBlank(message = "密码不能为空") private String password; /** * 手机号 */ @Pattern(regexp = "^[1]([3][0-9]{1}|59|58|88|89)[0-9]{8}$", message = "手机号格式有误") @Length(min = 11, max = 11, message = "手机号必须为11位") private String personPhone; /** * 邮箱 */ @Email(message = "邮箱格式有误") private String personEmail; /** * 资产 */ @Pattern(regexp = "^(([1-9]{1}\d*)|([0]{1}))(\.(\d){0,2})?$", message = "金额有误!必须是数字且最多保留两位小数") private String personMoney; /** * 照片 */ @Size(min = 1, max = 3, message = "集合长度的范围为1~3") @NotEmpty(message = "集合不能为空") private List<String> photoList; }
Controller
@RestController @RequestMapping("/person") @Validated public class PersonController { @GetMapping("/get") public DataResult get(@Range(max = 10, message = "age最大值为10") @RequestParam("age") Integer age,@NotBlank(message = "name不可以为空") @Length(min = 3, message = "name长度最少是3") >@RequestParam("name") String name) { return DataResult.success(); } @PostMapping("/post") public DataResult post(@Validated @RequestBody PersonBean person) { return DataResult.success(); } }
修改参数校验模式
SpringBoot默认的是对所有的实体类属性进行验证,之后才会抛出异常,这样效率就会变低,但是其实只要有一个验证失败,那么就代表这个请求失败,直接拒绝这个请求,所以创建一个配置类,完成一种新的校验模式(直接拷贝):
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@Configuration
public class ValidatorConfig {
/**
* validation默认会校验完所有字段,然后返回所有的验证失败信息。
* 可以通过一些简单的配置,开启Fail Fast模式,只要有一个验证失败就立即返回
*/
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation
.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
定义全局异常拦截,将异常中重要信息返回给前端
/**
* @Description TODO 全局异常管理
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public DataResult validateException(MethodArgumentNotValidException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<String> list = new ArrayList<>();
for (FieldError error : fieldErrors) {
list.add(error.getField() + error.getDefaultMessage());
}
return DataResult.custom(500, "参数有误!", list.get(0));
}
@ExceptionHandler(value = ConstraintViolationException.class)
public DataResult validateException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
List<String> list = new ArrayList<>();
for (ConstraintViolation<?> item : violations) {
list.add(item.getMessage());
}
return DataResult.custom(500, "参数有误!", list.get(0));
}
}
因为上面我们配置了校验模式:只要有一个验证失败就立即返回信息,所以这里返回的都是list.get(0);
验证
get请求
post请求
@Validated 分组验证
- 定义两个分组接口 : 定义两个interface,实现javax.validation.groups.Default接口:
public interface Add extends Default {
}
public interface Update extends Default {
}
- 在实体类中给属性添加验证注解的时候指定验证的分组
@Data
public class Person {
@NotEmpty(groups = Update.class, message = "更新时候id不能为空")
private Long id;
@NotEmpty(groups = {Add.class,Update.class}, message = "姓名不能为空")
private String name;
}
- 使用分组进行参数的验证
@RestController
@Slf4j
public class VerifyController {
@PostMapping(value = "/validated/add")
public void add(@Validated(value = Add.class) @RequestBody Person person) {
...
}
@PostMapping(value = "/validated/update")
public void update(@Validated(value = Update.class) @RequestBody Person person) {
...
}
}
自定义校验注解
- 定义一个注解
/**
* @Description TODO 身份证号校验注解
*/
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidate.class)
public @interface IdCard {
String message() default "身份证号码不合法";
Class<?>[] groups() default {}; //定义了验证分组,默认为空。
Class<? extends Payload>[] payload() default {}; //用于指定负载的类型,默认为空。
}
注解类里面前三个参数是固定必须的,直接复制即可,@Constraint(validatedBy = IdCardValidate.class)指定的是下面我们自己创建的校验逻辑实现类
/**
* @Description TODO 身份证校验逻辑
* ConstraintValidator<IdCard, String>
* 其中IdCard是自定义注解,String是需要校验的数据类型
*/
public class IdCardValidate implements ConstraintValidator<IdCard, String> {
@Override
public void initialize(IdCard constraintAnnotation) {
//该方法在校验器初始化时被调用。在这个例子中,该方法并未做任何初始化操作。
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//参数1:表示需要校验的值
//参数2:表示约束验证器的上下文
//这里只是举例,此处输入你的校验逻辑,成功返回true,否则返回false
String id = "123456789";
if (value.equals(id)) {
return true;
}
return false;
}
}
- 使用自定义注解
@IdCard
@NotBlank(message = "身份证为必填项")
private String idCard;