前言
在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有点麻烦:
验证代码繁琐,重复劳动
方法内代码显得冗长
每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码
叙述
Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator。
Bean Validation API是Java定义的一个验证参数的规范。
Hibernate Validator是Bean Validation API的一个实现。
@Valid和Validated的比较
Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。
@Valid : 没有分组功能,可以用在方法、构造函数、方法参数和成员属性(field)上,如果一个待验证的pojo类,其中还包含了待验证的对象,需要在待验证对象上注解@valid,才能验证待验证对象中的成员属性
@Validated :提供分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制,用在类型、方法和方法参数上。但不能用于成员属性(field)。
两者都可以用在方法入参上,但都无法单独提供嵌套验证功能,都能配合嵌套验证注解@Valid进行嵌套验证。
嵌套验证示例:
1
2
3
4
5
6
7
8
|
public class ClassRoom{
@NotNull
String name;
@Valid // 嵌套校验,校验参数内部的属性
@NotNull
Student student;
}
|
1
2
3
4
5
6
7
|
@GetMapping ( "/room" ) // 此处可使用 @Valid 或 @Validated, 将会进行嵌套校验
public String validator( @Validated ClassRoom classRoom, BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok" ;
}
|
BindingResult 的使用
BindingResult必须跟在被校验参数之后,若被校验参数之后没有BindingResult对象,将会抛出BindException。
1
2
3
4
5
6
7
|
@GetMapping ( "/room" )
public String validator( @Validated ClassRoom classRoom, BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok" ;
}
|
不要使用 BindingResult 接收String等简单对象的错误信息。简单对象校验失败,会抛出 ConstraintViolationException。主要就是接不着,你要写也算是没关系…
1
2
3
4
5
6
7
8
9
|
// ❌ 错误用法,也没有特别的错,只是 result 是接不到值。
@GetMapping ( "/room" )
@Validated // 启用校验
public String validator( @NotNull String name, BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok" ;
}
|
修改校验失败的提示信息
可以通过各个校验注解的message属性设置更友好的提示信息。
1
2
3
4
5
6
7
8
|
public class ClassRoom{
@NotNull (message = "Classroom name must not be null" )
String name;
@Valid
@NotNull
Student student;
}
|
1
2
3
4
5
6
7
8
|
@GetMapping ( "/room" )
@Validated
public String validator(ClassRoom classRoom, BindingResult result, @NotNull (message = "姓名不能为空" ) String name) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok" ;
}
|
message属性配置国际化的消息也可以的,message中填写国际化消息的code,在抛出异常时根据code处理一下就好了。
1
2
3
4
5
6
7
8
|
@GetMapping ( "/room" )
@Validated
public String validator( @NotNull (message = "demo.message.notnull" ) String name) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok" ;
}
|
1
2
3
4
5
|
// message_zh_CN.properties
demo.message.notnull=xxx消息不能为空
// message_en_US.properties
demo.message.notnull=xxx message must no be null
|
hibernate-validator 的使用
1.引入pom
1
2
3
4
5
|
< dependency >
< groupId >org.hibernate</ groupId >
< artifactId >hibernate-validator</ artifactId >
< version >5.3.1.Final</ version >
</ dependency >
|
2.dto入参对象属性加入注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Data
public class UserModel implements Serializable {
private String id;
@NotBlank (message = "用户名不能为空" )
private String name;
@NotNull (message = "性别不能不填" )
private Byte gender;
@NotNull (message = "年龄不能不填" )
@Min (value = 0 ,message = "年龄必须大于0岁" )
@Max (value = 150 ,message = "年龄必须小于150岁" )
private Integer age;
@NotBlank (message = "手机号不能不填" )
private String telphone;
private String registerMode;
private String thirdPartyId;
private String encrptPassward;
}
|
方法一:3.controller方法入参加入校验(@Validated )
1
2
3
4
5
6
7
|
@GetMapping ( "/getUser" )
public String validator( @Validated UserModel userModel , BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok" ;
}
|
方法二:3.自定义封装ValidatorImpl类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Component
public class ValidatorImpl implements InitializingBean{
private Validator validator;
//实现校验方法并返回校验结果
public ValidationResult validate(Object bean){
final ValidationResult result= new ValidationResult();
Set<ConstraintViolation<Object>> validate = validator.validate(bean);
if (validate.size()> 0 ) {
result.setHasError( true );
validate.forEach(constraintViolation->{
String errMsg=constraintViolation.getMessage();
String propertyName=constraintViolation.getPropertyPath().toString();
result.getErrorMsgMap().put(propertyName,errMsg);
});
}
return result;
}
@Override
public void afterPropertiesSet() throws Exception {
this .validator= Validation.buildDefaultValidatorFactory().getValidator();
}
}
|
方法二:4.自定义封装ValidationResult 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class ValidationResult {
public boolean hasError= false ;
private Map<String,String> errorMsgMap= new HashMap<>();
//实现通用的通过格式化字符串信息获取错误结果的msg方法
public String getErrMsg(){
return StringUtils.join(errorMsgMap.values().toArray(), "," );
}
public boolean isHasError() {
return hasError;
}
public void setHasError( boolean hasError) {
this .hasError = hasError;
}
public Map<String, String> getErrorMsgMap() {
return errorMsgMap;
}
public void setErrorMsgMap(Map<String, String> errorMsgMap) {
this .errorMsgMap = errorMsgMap;
}
}
|
5.controller方法入参加入校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Autowired
private ValidatorImpl validator;
@Override
@Transactional (rollbackFor = Exception. class )
public void register(UserModel userModel) throws BusinessException {
UserDo userDo= new UserDo();
if (userModel == null ) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
//validate进行入参校验
ValidationResult validate = validator.validate(userModel);
if (validate.isHasError()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,validate.getErrMsg());
}
}
|
Bean Validation 的约束
-
@Null
被注释的元素必须为 null -
@NotNull
被注释的元素必须不为 null -
@AssertTrue
被注释的元素必须为 true -
@AssertFalse
被注释的元素必须为 false -
@Min(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值 -
@Max(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值 -
@DecimalMin(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值 -
@DecimalMax(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值 -
@Size(max, min)
被注释的元素的大小必须在指定的范围内 -
@Digits (integer, fraction)
被注释的元素必须是一个数字,其值必须在可接受的范围内 -
@Past
被注释的元素必须是一个过去的日期 -
@Future
被注释的元素必须是一个将来的日期 -
@Pattern(value)
被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的约束
Hibernate Validator 附加的 constraint:
-
@Email
被注释的元素必须是电子邮箱地址 -
@Length
被注释的字符串的大小必须在指定的范围内 -
@NotEmpty
被注释的字符串的必须非空 -
@Range
被注释的元素必须在合适的范围内
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/zmh458/article/details/96173583