vlambda博客
学习文章列表

spring validation的使用与自定义校验注解

前言

前端录入数据,在后台进行校验工作是必不可少的。例如:为空校验,邮箱格式校验,手机号校验等。
后端校验的目的是避免用户绕过浏览器,使用http工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中,如果数据库中出现一个非法的邮箱格式,也会让运维人员头疼不已。


下面介绍spring 框架中的自动校验机制。

框架提供的校验注解

注解 说明
@NotNull 值不能为空
@Null 值必须为空
@Pattern(regexp=) 字符串必须匹配正则表达式
@Size(min=,max=) 集合的元素数量必须在min和max之间
@CreditCardNumber(ignoreNonDigitCharacters) 字符串必须是信用卡号(按美国标准校验)
@Email 字符串必须是Email邮箱
@Length(min=,max=) 检查字符串的长度
@NotBlank 字符串必须有字符
@NotEmpty 字符串不为null,集合有元素
@Range(min=,max=) 数字必须大于等于min,小于等于max
@SafeHtml 字符串是安全的html
@URL 字符串是合法的URL
@AssertFalse 值必须是false
@AssertTrue 值必须是true
@DecimalMax(value=,inclusive=) 值必须小于等于(inclusive=true)/小于(inclusive=false) value属性指定的值,可以注解在字符串类型的属性上
@DecimalMin(value=,inclusive=) 值必须大于等于(inclusive=true)/大于(inclusive=false) value属性指定的值,可以注解在字符串类型的属性上
@Digits(integer=,fraction=) 数字格式检查,integer指定整数部分的最大长度,fraction指定小数部分的最大长度
@Future 值必须是未来的日期
@Past 值必须是过去的日期
@Max(value=) 值必须小于等于value指定的值,不能注解在字符串类型的属性上
@Min(value=) 值必须大于等于value指定的值,不能注解在字符串类型的属性上


引入依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.6.RELEASE</version></dependency><!--用lombok ,让代码更简洁--><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version></dependency>


实体类

@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class User implements Serializable { private static final long serialVersionUID = -8441401775719174836L;
private Integer id; private String userName;
@NotBlank(message = "密码不能为空") private String password;
@Past(message = "生日必须是过去的时间") private Date birthDay;}

控制层

import javax.validation.Valid;import org.springframework.validation.BindingResult;/*** 说明:* 添加 BindingResult参数后,就算验证不通过,也能继续执行方法体* BindingResult的参数的作用:* 方便记录日志(如果不想继续向下执行,可以直接再抛异常)* @param user* @param errors* @return*/@PostMapping()@JsonView(User.UserSimpleView.class)public Object create(@Valid @RequestBody User user, BindingResult result){ // 判断BindingResult是否保存错误的验证信息,如果有,则直接return if (errors.hasErrors()) { Map<String, String> errors = Maps.newHashMap(); result.getFieldErrors().stream().forEach(f -> errors.put(f.getField(), f.getDefaultMessage()));  return errors; }  //对 user的处理,然后返回
return user;}


自定义校验注解

自定义注解用途:校验字段的在数据库里的唯一性等


定义注解类

** * 自定义校验注解 * 类似与 @NotEmpty * * @Constraint(validatedBy = MyConstraintValidator.class) 指明这是一个校验的注解 * validatedBy = MyConstraintValidator.class 指定由MyConstraintValidator类去校验 * * 必须定义三个成员 message,groups,payload * */@Target({ElementType.METHOD, ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy = MyConstraintValidator.class)public @interface MyConstraint { String message() default "{javax.validation.constraints.NotEmpty.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { };}


定义校验类

/** * 不用添加 @Component注解 * spring会扫描实现了ConstraintValidator 接口的类后,然后自动管理 * * ConstraintValidator<MyConstraint, String>: * 泛型参数说明: * 第一个参数 指定校验MyConstraint注解的字段 * 第二参数 注解修改字段的类型 * @author chennan * @date 2019.7.22 22:19 */public class MyConstraintValidator implements ConstraintValidator<MyConstraint, String> {
/** * 可以注入任何spring管理的服务 */ @Autowired private HelloService helloService;
@Override public boolean isValid(String value, ConstraintValidatorContext context) { System.out.println("------------MyConstraintValidator---------"); System.out.println(value); System.out.println(helloService.greeting("tom")); System.out.println("------------MyConstraintValidator---------");
//true-校验通过 false-校验不通过 return false; }
@Override public void initialize(MyConstraint constraintAnnotation) { System.out.println("my validator init"); }}


使用自定义校验注解

public class User implements Serializable{ private static final long serialVersionUID = -8441401775719174836L; @MyConstraint(message = "测试自定义注解") private String userName;}