springboot使⽤ValidationAPI和全局异常优雅的校验⽅法参数⽬录
⼀、为什么使⽤ Validation 来验证参数
通常我们在使⽤spring框架编写接⼝时,对于部分接⼝的参数我们要进⾏判空或者格式校验来避免程序出现异常。那是我们⼀般都是使⽤if-else逐个对参数进⾏校验。这种⽅法按逻辑来说也是没有问题的,同样也能实现预期效果。但是,这样的代码从可读性以及美观程序来看,是⾮常糟糕的。那么,我们就可以使⽤@valid注解来帮助我们优雅的校验参数。
⼆、如何使⽤Validation相关注解进⾏参数校验
①为实体类中的参数或者对象添加相应的注解;②在控制器层进⾏注解声明,或者⼿动调⽤校验⽅法进⾏校验;③对异常进⾏处理;三、Validation类的相关注解及描述
验证注解验证的数据类型说明
@AssertFalse Boolean,boolean验证注解的元素值是false
@AssertTrue Boolean,boolean验证注解的元素值是true
@NotNull任意类型验证注解的元素值不是null
@Null任意类型验证注解的元素值是null
@Min(value=值)BigDecimal,BigInteger, byte,short, int, long,
等任何Number或CharSequence(存储的是数字)
⼦类型
验证注解的元素值⼤于等于@Min指定的value值
@Max(value=值)和@Min要求⼀样验证注解的元素值⼩于等于@Max指定的value值
@DecimalMin(value=
值)
和@Min要求⼀样验证注解的元素值⼤于等于@ DecimalMin指定的value值
@DecimalMax(value=
值)
和@Min要求⼀样验证注解的元素值⼩于等于@ DecimalMax指定的value值
@Digits(integer=整数
位数, fraction=⼩数位
数)
和@Min要求⼀样验证注解的元素值的整数位数和⼩数位数上限
@Size(min=下限, max=上限)字符串、Collection、Map、数组等
验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、
集合⼤⼩
@Past java.util.Date,java.util.Calendar;Joda Time类库的
⽇期类型
验证注解的元素值(⽇期类型)⽐当前时间早
@Future与@Past要求⼀样验证注解的元素值(⽇期类型)⽐当前时间晚
@NotBlank CharSequence⼦类型验证注解的元素值不为空(不为null、去除⾸位空格后长度为0),不同于@NotEmpty,@NotBlank只应⽤于字符串且在⽐较时会去除字符串的⾸位空格
@Length(min=下限,
max=上限)
CharSequence⼦类型验证注解的元素值长度在min和max区间内
@NotEmpty CharSequence⼦类型、Collection、Map、数组验证注解的元素值不为null且不为空(字符串长度不为0、集合⼤⼩不为0)
@Range(min=最⼩值, max=最⼤值)BigDecimal,BigInteger,CharSequence, byte,
short, int, long等原⼦类型和包装类型
验证注解的元素值在最⼩值和最⼤值之间
@Email(regexp=正则
表达式,flag=标志的模CharSequence⼦类型(如String)
验证注解的元素值是Email,也可以通过regexp和flag指定⾃定义的email
表达式,flag=标志的模式)CharSequence⼦类型(如String)
验证注解的元素值是Email,也可以通过regexp和flag指定⾃定义的email
格式
@Pattern(regexp=正
则表达式,flag=标志的
模式)
String,任何CharSequence的⼦类型验证注解的元素值与指定的正则表达式匹配
@Valid任何⾮原⼦类型指定递归验证关联的对象如⽤户对象中有个地址对象属性,如果想在验证⽤户对象时⼀起验证地址对象的话,在地址对象上加@Valid注解即可级联验证
验证注解验证的数据类型说明
此处只列出Validator提供的⼤部分验证约束注解,请参考hibernate validator官⽅⽂档了解其他验证约束注解和进⾏⾃定义的验证约束注解定义。
四、使⽤ Validation API 进⾏参数效验步骤
整个过程如下图所⽰,⽤户访问接⼝,然后进⾏参数效验。 对于GET请求的参数可以使⽤@validated注解配合上⾯相应的注解进⾏校验或者按照原先if-else⽅式进⾏效验。⽽对于POST请求,⼤部分是以表单数据即以实体对象为参数,可以使⽤@Valid注解⽅式进⾏效验。
如果效验通过,则进⼊业务逻辑,否则抛出异常,交由全局异常处理器进⾏处理。
五、 Spring Validation的三种校验⽅式
第⼀种:在Controller⽅法参数前加@Valid注解——校验不通过时直接抛异常,get请求直接在平⾯参数前添加相应的校验规则注解,使⽤这种的话⼀般结合统⼀异常处理进⾏处理;
第⼆种:在Controller⽅法参数前加@Valid注解,参数后⾯定义⼀个BindingResult类型参数——执⾏时会将校验结果放进bindingResult ⾥⾯,⽤户⾃⾏判断并处理。
1/**
* 将校验结果放进BindingResult⾥⾯,⽤户⾃⾏判断并处理
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 *
* @param userInfo
* @param bindingResult
* @return
*/
@PostMapping("/testBindingResult")
public String testBindingResult(@RequestBody@Valid UserInfo userInfo, BindingResult bindingResult) { // 参数校验
if(bindingResult.hasErrors()) {
String messages = AllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.reduce((m1, m2) -> m1 + ";"+ m2)
.orElse("参数输⼊有误!");
//这⾥可以抛出⾃定义异常,或者进⾏其他操作
throw new IllegalArgumentException(messages);
}
return"操作成功!";
}
这⾥我们是直接抛出了异常,如果没有进⾏全局异常处理的话,接⼝将会返回如下信息:
第三种:⽤户⼿动调⽤对应API执⾏校验——Validation.buildDefault ValidatorFactory().getValidator().validate(xxx)这种⽅法适⽤于校验任意⼀个有valid注解的实体类,并不仅仅是只能校验接⼝中的参数;
这⾥我提取出⼀个⼯具类,如下:
MyValidationUtils.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import java.util.Set;
/**
* ⼿动调⽤api⽅法校验对象
*/
public class MyValidationUtils {
public static void validate(@Valid Object user) {
Set<ConstraintViolation<@Valid Object>> validateSet = Validation.buildDefaultValidatorFactory() .getValidator()
.validate(user, new Class[0]);
if(!CollectionUtils.isEmpty(validateSet)) {
String messages = validateSet.stream()
18 19 20 21 22 23 24 25 .map(ConstraintViolation::getMessage)
.reduce((m1, m2) -> m1 + ";"+ m2)
.orElse("参数输⼊有误!");validation框架
throw new IllegalArgumentException(messages);
}
}
}
五、springboot项⽬中实战演练
spring-boot-starter-web依赖已经集成相关jar,⽆需额外引⼊。
1.对实体类的变量进⾏注解标注
实体类中添加 @Valid 相关验证注解,并在注解中添加出错时的响应消息。User.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21import org.straints.Length;
import javax.validation.Valid;
import straints.NotBlank;
import straints.NotNull;
import straints.Pattern;
@Data
public class User {
@NotBlank(message = "姓名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6, max = 16, message = "密码长度为6-16位")
private String password;
@Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "⼿机号格式不正确") private String phone;
// 嵌套必须加 @Valid,否则嵌套中的验证不⽣效
@Valid
@NotNull(message = "userinfo不能为空")
private UserInfo userInfo;
}
如果是嵌套的实体对象,并且也要校验该对象,则需要在最外层属性上添加 @Valid 注解UserInfo.class
1 2 3 4 5 6 7 8 9 10import straints.Max;
import straints.NotBlank;
@Data
public class UserInfo {
@NotBlank(message = "年龄不为空")
@Max(value = 18, message = "不能超过18岁") private String age;
@NotBlank(message = "性别不能为空")
private String gender;
11}
2.创建⾃定义异常
⾃定义异常类,⽅便我们处理⼿动抛出的异常。
1 2 3 4 5 6 7 8 9public class ParamaErrorException extends RuntimeException { public ParamaErrorException() {
}
public ParamaErrorException(String message) {
super(message);
}
}
3.⾃定义响应枚举类
定义⼀个返回信息的枚举类,⽅便我们快速响应信息,不必每次都写返回消息和响应码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22public enum ResultEnum {
SUCCESS(1000, "请求成功"),
PARAMETER_ERROR(1001, "请求参数有误!"), UNKNOWN_ERROR(9999, "未知的错误!");
private Integer code;
private String message;
ResultEnum(Integer code, String message) {
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
4.⾃定义响应对象类
创建⽤于返回调⽤⽅的响应信息的实体类。
1 2 3 4 5 6 7 8import com.ums.ResultEnum; import lombok.Data;
@Data
public class ResponseResult {
private Integer code;
private String msg;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论