@Valid 和 @Validated 注解用法
没有使用注解验证
要求:员工的名称不能为空,且长度不能超过10个字符,那么我们以前的做法大致如下:
写完,我们启动项目测试下:
(1)名称为空情况
(2)正常情况
(3)超过长度情况
可以看到,和我们料想中的一样,毫无问题。
除了名称外,我们规定年龄也是必填项,且范围在1到100岁,那么此时,我们需要增加对应判定代码如下:
那么问题来了,现在员工对象 Employee 就 2 个字段,我们就写了 10 多行的代码验证,要是有20个字段,岂不是要写 100 多行代码?通常来说,当一个方法中的无效业务代码量过多时,往往代码设计有问题,当然这不是我们所想看到都结果。
那么如何解决呢?首先大家应该会想到将对应的验证过程抽成一个验证方法,如下:
这样来看,我们的业务方法就清爽多了。
但这种方式只是抽了一个方法,有一种换汤不换药的感觉,虽然业务方法看起来清爽了很多,但书写代码量并没有下降,反而还多出了一个方法,这也不是我们理想中的样子。
@Valid 使用
此时,我们引出 Spring 中的 @valid 注解,这些问题就可以迎刃而解了,具体如下:
首先,我们在 Maven 配置中引入 @valid 的依赖:
如果你是 springboot 项目,那么可以不用引入了,已经引入了,他就存在于最核心的 web 开发包里面。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
如果你不是 springboot 项目,那么引入下面依赖即可:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
那么针对上面情景,我们可以对我们的代码进行优化了。
首先我们在类的属性上打上如下注解:
然后再 Controller 对应方法上,对这个员工标上 @Valid 注解,表示我们对这个对象属性需要进行验证
既然验证,那么就肯定会有验证结果,所以我们需要用一个东西来存放验证结果,做法也很简单,在参数直接添加一个BindingResult,具体如下:
对应获取验证结果的代码如下:
OK ! 万事俱备 !我们进行测试下:
(1)名称为空
(2)名称正常,年龄为空
(3)名称超出范围,年龄正常
(4)名称正常,年龄超出范围
可以看到,代码不但简洁了很多,结果和预期的也一模一样!很棒吧!!
常用注解
@Validated 使用
上面,我们讲述了 @Valid 注解,现在我们来说说 @Validated 这个注解,在我看来,@Validated 是在 @Valid 基础上,做的一个升级版。
我们可以看到,我们在使用 @Valid 进行验证的时候,我们需要用一个对象去接收校验结果,最后根据校验结果判断,从而提示用户。
如果我们把手动校验的这段代码删除或注释掉,那么即使当我们的字段不满足规则时,方法种的程序也是能够被执行的。
比如,我们将字段值置空时,正常情况是会进行提示的。
当我们把校验逻辑注释掉后,再次执行上面的请求后。
可以看到我们的程序继续往后面去执行完成了。
现在,我们去掉方法参数上的 @Valid 注解和其配对的 BindingResult 对象,
然后再校验的对象前面添加上 @Validated 注解。
这个时候,我们再次请求,可以看到,我们请求报400错误了。
而我们通过程序的异常日志来看,提示说是 age 和 name 字段为了空,致使请求失败。
那么,从这里我们可以得知,当我们的数据存在校验不通过的时候,程序就会抛出
org.springframework.validation.BindException 的异常。
在实际开发的过程中,我们肯定不能讲异常直接展示给用户,而是给能看懂的提示。
于是,我们不妨可以通过捕获异常的方式,将该异常进行捕获。
首先我们创建一个校验异常捕获类 ValidExceptionHandler ,然后打上 @RestControllerAdvice 注解,该注解表示他会去抓所有 @Controller 标记类的异常,并在异常处理后返回以 JSON 或字符串的格式响应前端。
在异常捕捉到后,我们同上面的 @valid 校验一样,只返回第一个错误提示。
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ValidExceptionHandler {
@ExceptionHandler(BindException.class)
public String validExceptionHandler(BindException exception) {
return exception.getAllErrors().get(0).getDefaultMessage();
}
}
部分常见注解如下:
注解 | 功能 |
---|---|
@AssertFalse | 可以为null,如果不为null的话必须为false |
@AssertTrue | 可以为null,如果不为null的话必须为true |
@DecimalMax | 设置不能超过最大值 |
@DecimalMin | 设置不能超过最小值 |
@Digits | 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内 |
@Future | 日期必须在当前日期的未来 |
@Past | 日期必须在当前日期的过去 |
@Max | 最大不得超过此最大值 |
@Min | 最大不得小于此最小值 |
@NotNull | 不能为null,可以是空 |
@Null | 必须为null |
@Pattern | 必须满足指定的正则表达式 |
@Size | 集合、数组、map等的size()值必须在指定范围内 |
必须是email格式 | |
@Length | 长度必须在指定范围内 |
@NotBlank | 字符串不能为null,字符串trim()后也不能等于“” |
@NotEmpty | 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“” |
@Range | 值必须在指定范围内 |
@URL | 必须是一个URL |
注解简单演示:
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
public class UserVo {
@Length(min = 4, max = 12, message = "账号长度必须位于4到10之间")
private String userCode;
@NotBlank(message = "用户名不能为空")
private String userName;
@Email(message = "邮箱格式有误")
private String email;
@Pattern(regexp = "^1[3456789]\\d{9}$", message = "手机号格式错误")
private String phone;
/**
* 0:普通用户;1:会员用户;2:超级VIP用户,只针对字符串数据,针对数字验证报错
*/
@Pattern(regexp = "[012]", message = "用户类型有误,请输入0:普通用户;1:会员用户;2:超级VIP用户")
private String type;
}
@Valid 和 @Validated 比较
(1)@Valid 和 @Validated 两者都可以对数据进行校验,待校验字段上打的规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效;
(2)@Valid 进行校验的时候,需要用 BindingResult 来做一个校验结果接收。当校验不通过的时候,如果手动不 return ,则并不会阻止程序的执行;
(3)@Validated 进行校验的时候,当校验不通过的时候,程序会抛出400异常,阻止方法中的代码执行,这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。
(4)总体来说,@Validated 使用起来要比 @Valid 方便一些,它可以帮我们节省一定的代码,并且使得方法看上去更加的简洁。
原文链接:https://blog.csdn.net/sunnyzyq/article/details/103527380