SpringValidation

一、概述

​ JSR 303中提出了Bean Validation,表示JavaBean的校验,Hibernate Validation是其具体实现,并对其进行了一些扩展,添加了一些实用的自定义校验注解。

​ Spring中集成了这些内容,你可以在Spring中以原生的手段来使用校验功能,当然Spring也对其进行了一点简单的扩展,以便其更适用于Java web的开发。

在开发中,我们经常遇到参数校验的需求,比如用户注册的时候,要校验用户名不能为空、用户名长度不超过20个字符、手机号是合法的手机号格式等等。如果使用普通方式,我们会把校验的代码和真正的业务处理逻辑耦合在一起,而且如果未来要新增一种校验逻辑也需要在修改多个地方。而spring validation允许通过注解的方式来定义对象校验规则,把校验和业务逻辑分离开,让代码编写更加方便。Spring Validation其实就是对Hibernate Validator进一步的封装,方便在Spring中使用。在Spring中有多种校验的方式。

注: 之前在controller中用if进行参数校验,现在可以换成springValidation框架来进行参数校验(只需要在Controller的方法参数上和实体类上加上对应的注解就行)

1.1、Spring 校验使用场景

Spring 常规校验(Validator):通过实现org.springframework.validation.Validator接口,然后在代码中调用这个类

Spring 数据绑定(DataBinder):按照Bean Validation方式来进行校验,即通过注解的方式。

Spring Web 参数绑定(WebDataBinder):按照Bean Validation方式来进行校验,即通过注解的方式。

Spring WebMVC/WebFlux 处理方法参数校验:基于方法实现校验

下边列出了几种使用场景:

(1)SpringMVC输入参数校验

(2)Spring管理的bean方法执行参数校验

(3)Spring初始化过程验证bean的属性

1.2、JSR 303 Bean Validation

​ JSR 303中提供了诸多实用的校验注解,这里简单罗列:

//校验类型(message="错误提示")
//1、@Null                       校验对象是否为null
//2、@NotNull                    校验对象是否不为null
//3、@NotBlank                   校验字符串去头尾空格后的长度是否大于0或是否为null
//4、@NotEmpty                   校验字符串是否为null或是否为empty
//
//5、@AssertTrue                 校验Boolean是否为true
//6、@AssertFalse                校验Boolean是否为false
//
//7、@UniqueElements             校验数组/集合的元素是否唯一
//8、@Size(min,max)              校验数组/集合/字符串长度是否在范围之内
//9、@Length(min,max)            校验数组/集合/字符串长度是否在范围之内
//10、@Range(min,max)            校验Integer/Short/Long是否在范围之内
//11、@Min(number)               校验Integer/Short/Long是否大于等于value
//12、@Max(number)               校验Integer/Short/Long是否小于等于value
//13、@Positive                  校验Integer/Short/Long是否为正整数
//14、@PositiveOrZero            校验Integer/Short/Long是否为正整数或0
//15、@Negative                  校验Integer/Short/Long是否为负整数
//16、@NogativeOrZero            校验Integer/Short/Long是否为负整数或0
//
//17、@DecimalMin(decimal)       校验Float/Double是否大于等于value
//18、@DecimalMax(decimal)       校验Float/Double是否小于等于value
//19、@Digits(integer,fraction)  校验数字是否符合整数位数精度和小数位数精度
//
//20、@Past(date)                校验Date/Calendar是否在当前时间之前
//21、@PastOrPresent(date)       校验Date/Calendar是否在当前时间之前或当前时间
//22、@Future(date)              校验Date/Calendar是否在当前时间之后
//23、@FutureOrPresent(date)     校验Date/Calendar是否在当前时间之后或当前时间
//
//24、@Email                     校验字符串是否符合电子邮箱格式
//25、@URL(protocol,host,port)   校验字符串是否符合URL地址格式
//26、@CreditCardNumber          校验字符串是否符合信用卡号格式
//
//27、@Pattern(regexp)           校验字符串是否符合正则表达式的规则
//
//除此之外,我们还可以自定义一些数据校验规则

举例:

@size (min=3, max=20,message="用户名长度只能在3-20之间")

@size (min=6, max=20,message="密码长度只能在6-20之间")

@pattern(regexp="[a-za-z0-9._%+-]+@[a-za-z0-9.-]+\\.[a-za-z]{2,4}",message="邮件格式错误")

@Length(min =5, max =20, message ="用户名长度必须位于5到20之间")

@Email(message ="比如输入正确的邮箱")

@NotNull(message ="用户名称不能为空")

@Max(value =100, message ="年龄不能大于100岁")

@Min(value=18,message="必须年满18岁!")

@AssertTrue(message ="bln4 must is true")

@AssertFalse(message ="blnf must is falase")

@DecimalMax(value="100",message="decim最大值是100")

@DecimalMin(value="100",message="decim最小值是100")

@NotNull(message ="身份证不能为空")

@Pattern(regexp="^(\\d{18,18}|\\d{15,15}|(\\d{17,17}[x|X]))$", message="身份证格式错误")

✈ 空值检查

注解

说明

@NotBlank

用于字符串,字符串不能为null 也不能为空字符串

@NotEmpty

字符串同上,对于集合(Map,List,Set)不能为空,必须有元素

@NotNull

不能为 null

@Null

必须为 null

✈ 数值检查

注解

说明

@DecimalMax(value)

被注释的元素必须为数字,其值必须小于等于指定的值

@DecimalMin(value)

被注释的元素必须为数字,其值必须大于等于指定的值

@Digits(integer, fraction)

被注释的元素必须为数字,其值的整数部分精度为 integer,小数部分精度为 fraction

@Positive

被注释的元素必须为正数

@PositiveOrZero

被注释的元素必须为正数或 0

@Max(value)

被注释的元素必须小于等于指定的值

@Min(value)

被注释的元素必须大于等于指定的值

@Negative

被注释的元素必须为负数

@NegativeOrZero

被注释的元素必须为负数或 0

✈ Boolean 检查

注解

说明

@AssertFalse

被注释的元素必须值为 false

@AssertTrue

被注释的元素必须值为 true

✈ 长度检查

注解

说明

@Size(min,max)

被注释的元素长度必须在 min 和 max 之间,可以是 String、Collection、Map、数组

✈ 日期检查

注解

说明

@Future

被注释的元素必须是一个将来的日期

@FutureOrPresent

被注释的元素必须是现在或者将来的日期

@Past

被注释的元素必须是一个过去的日期

@PastOrPresent

被注释的元素必须是现在或者过去的日期

✈ 其他检查

注解

说明

@Email

被注释的元素必须是电子邮箱地址

@Pattern(regexp)

被注释的元素必须符合正则表达式

更简洁的参数校验,使用 SpringBoot Validation 对参数进行校验-腾讯云开发者社区-腾讯云

1.3、SpringValidation核心API

二、测试案例:
2.1、需要的依赖
<!--  Spring Validation 校验框架 -->
<!-- validator校验相关依赖 -->
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><!--<version>7.0.5.Final</version>--><version>6.2.5.Final</version>
</dependency>
<dependency><groupId>org.glassfish</groupId><artifactId>jakarta.el</artifactId><version>4.0.1</version>
</dependency>
<dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>2.0.2</version>
</dependency>

或者直接使用springBoot的启动器依赖这些内容:

<!--校验组件-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>${spring-boot.version}</version>
</dependency>
<!--web组件-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring-boot.version}</version>
</dependency>
2.1.实体类:
package com.zyq.validation.pojo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.*;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {@NotNull(message = "用户名不能为null") //SpringValidation框架提供的检测注解@NotBlank(message="名字不能为空")//SpringValidation框架提供的检测注解@Size(min=2,max=5,message = "名字字符过多")private String  userName;@Min(value = 0, message = "年龄不能小于0") //SpringValidation框架提供的检测注解@Max(value = 150,message = "年龄不能大于150") //SpringValidation框架提供的检测注解private int age;private String pwd;@Pattern(regexp = "^1(3\\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$", message = "手机号格式错误")private String phone;@Email(message = "邮箱格式错误") //SpringValidation框架提供的检测注解private String email;@Past(message = "生日必须早于当前时间") //SpringValidation框架提供的检测注解private Date birth;@PositiveOrZero(message = "余额必须大于等于0") //SpringValidation框架提供的检测注解private Double money;public User(String userName, String pwd) {this.userName = userName;this.pwd = pwd;}
}
2.2.TestController
package com.zyq.validation.controller;
import com.zyq.validation.pojo.entity.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {@PostMapping(value = "/reg")public User test(@Validated User user){//index.html访问出来后提交数据就会报400的异常,提示名字字符过多,年龄超过限制return user;}
}
//@RequestBody注解解释:
//@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的,所以只能接收post方式数据);
//在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
//注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。
//注:当同时使用@RequestParam()和@RequestBody时,@RequestParam()指定的参数可以是普通元素、
//数组、集合、对象等等(即:当,@RequestBody 与@RequestParam()可以同时使用时,原SpringMVC接收
//参数的机制不变,只不过RequestBody 接收的是请求体里面的数据;而RequestParam接收的是key-value
//里面的参数,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)。
//即:如果参数时放在请求体中,传入后台的话,那么后台要用@RequestBody才能接收到;如果不是放在
//请求体中的话,那么后台接收前台传过来的参数时,要用@RequestParam来接收,或则形参前什么也不写也能接收。
//注:如果参数前写了@RequestParam(xxx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通
//过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。
//
//注:如果参数前不写@RequestParam(xxx)的话,那么就前端可以有可以没有对应的xxx名字才行,如果有xxx名/的话,那么就会自动匹配;没有的话,
/请求也能正确发送。
2.3.index.html:
<html xmlns="http://www.w3.org/1999/html">
<body>
<h1>hello word!!!</h1>
<p>this is a html zyq-page</p>
<meta http-equiv="Content-Type" content="application/json; charset=utf-8">
<!--<meta http-equiv="Content-Type" content="text/html; charset=utf-8">-->
<form action="/reg" method="post"><label for="un">用户名:</label> <input id="un" name="userName" type="text" value="zy_qqq1"/><br/><label for="age">年龄:</label> <input id="age" name="age" type="text" value="167"/><br/><label for="pwd">密码:</label> <input id="pwd" name="pwd" type="password" value="123456"/><br/><label for="phone">电话:</label> <input id="phone" name="phone" type="text" value="13367904423"/><br/><input type="submit" value="注册">
</form>
</body>
</html>

2.4.用浏览器测试:

测试地址:   localhost:8080

三、给案例添加Junit
3.1..ValidationConfig:
package com.zyq.validation.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;//有些案例没有这个配置类
@Configuration
@ComponentScan("com.zyq.validation")
public class ValidationConfig {@Beanpublic LocalValidatorFactoryBean validator() {return new LocalValidatorFactoryBean();}}
3.2.UserService1.java
package com.zyq.validation.service;
import com.zyq.validation.pojo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;
@Service
public class UserService1 {@Autowiredprivate Validator validator;public boolean validateUserByValidator(User user){BindException bindException=new BindException(user, user.getUserName());        validator.validate(user, bindException);//进行用户属性的验证return bindException.hasErrors();}}
3.3.测试类:
package com.zyq.validation.test;
import com.zyq.validation.configuration.ValidationConfig;
import com.zyq.validation.pojo.entity.User;
import com.zyq.validation.service.UserService1;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@SpringBootTest
public class TestValidation {public static void main(String[] args) {test1();}private static void test1() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);UserService1 myService = context.getBean(UserService1.class);User user = new User();user.setAge(-1);boolean validator = myService.validateUserByValidator(user);System.out.println(validator);}//Exception in thread "main" java.lang.IllegalArgumentException: Object name must not be null// at com.zyq.validation.service.UserService.validateUserByValidator(UserService.java:16)// at com.zyq.validation.test.testValidation.main(testValidation.java:17)}
3.4.jakarta.validator测试

A.UserService2:

package com.zyq.validation.service;
import com.zyq.validation.pojo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.ConstraintViolation;
import java.util.Set;
//import jakarta.validation.Validator;@Service
public class UserService2 {//javax.validation.Validator这种方式对于初学者不建议了解// (建议先了解UserService1中的org.springframework.validation.Validator)@Autowired//private jakarta.validation.Validator validator;private javax.validation.Validator validator;public boolean validateUserByJavaXValidator(User user){Set<ConstraintViolation<User>> sets=validator.validate(user);for(ConstraintViolation c:sets){System.err.println("------------------");System.err.println(c.getMessage());System.err.println("------------------");}return sets.isEmpty();}
}

B.TestValidation

四、用apifox测试:
4.1.TestController

给TestController添加方法

4.2. apifox:

给edge浏览器安装apifox插件之后根据下边方式测试:

测试结果:

4.3.统一异常处理

给项目中添加统一异常处理类:

@ControllerAdvice
@Slf4j
public class GlobalException {//用本类GlobalException的catchMethodArgumentNotValidException处理所有controller类的MethodArgumentNotValidException//编写完后测试,好像没效果//catchMethodArgumentNotValidException@ExceptionHandler(MethodArgumentNotValidException.class)//捕获所有controller的MethodArgumentNotValidException异常@ResponseBodypublic ResponseEntity<Object> exception(MethodArgumentNotValidException e, HttpServletRequest request) {Map<String, String> result = new HashMap<>();BindingResult bindingResult = e.getBindingResult();//获取异常结果信息//request.getMethod():请求方式,request.getRequestURI() 请求路径 //比如:  post    /reg2log.error("请求[ {} ] {} 的参数校验发生错误", request.getMethod(), request.getRequestURL());for (ObjectError objectError : bindingResult.getAllErrors()) {FieldError fieldError = (FieldError) objectError;//验证失败后,Spring会生成一个FieldError对象,其中包含了错误的详细信息//fieldError.getField():获取引发FieldError的被拒绝的key(方法返回的是导致FieldError的那个字段的字段名)//fieldError.getRejectedValue():获取引发FieldError的被拒绝的value(方法返回的是导致FieldError的那个字段的值,即那个无法通过验证的值。)//fieldError.getDefaultMessage(): 获取到错误提示log.error("参数 {} = {} 校验错误:{}", fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());result.put(fieldError.getField(), fieldError.getDefaultMessage());}// 一般项目都会有自己定义的公共返回实体类,这里直接使用现成的 ResponseEntity 进行返回,同时设置 Http 状态码为 400return ResponseEntity.badRequest().body(result);}
}

测试结果如下:

五、ajax测试:
5.1.下载jquery脚本

下载jquery脚本文件放到resources/static文件夹中。

用index2.html测试TestController中的test2方法:  test2方法比test1方法多了一个@RequestBody

5.2.编写index2.html:
<!DOCTYPE html>
<html>
<head>    <meta http-equiv="Content-Type" content="application/json;charset=utf-8">   <meta charset="UTF-8"><script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<h1>hello word!!!</h1>
<p>this is a html zyq-page</p>
<!-- [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public
com.zyq.validation.pojo.entity.User com.zyq.validation.controller.TestController.test2(com.zyq.validation.pojo.entity.User):
[Field error in object 'user' on field 'userName': rejected value [zyq_34324];
codes [Size.user.userName,Size.userName,Size.java.lang.String,Size];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [user.userName,userName]; arguments []; default message [userName],5,2]; default message [名字字符过多(2到5个字符)]] ]-->
<!--application/json不起作用--><!--<form action="/reg2"  id="myForm" method="post" enctype="application/json">-->
<form onsubmit="return false" method="post" id="myForm"><label for="un">用户名:</label> <input id="un" name="userName" type="text" value="zyq_34324"/><br/><label for="age">年龄:</label> <input id="age" name="age" type="text" value="16"/><br/><label for="pwd">密码:</label> <input id="pwd" name="pwd" type="password" value="123"/><br/><label for="phone">电话:</label> <input id="phone" name="phone" type="text" value="13367904423"/><br/><input type="submit" id="sub"  value="注册2">
</form>
</body>
<script>//定义serializeObject方法,序列化表单$.fn.serializeObject = function () {var o = {};var a = this.serializeArray();$.each(a, function () {if (o[this.name]) {if (!o[this.name].push) {o[this.name] = [o[this.name]];}o[this.name].push(this.value || '');} else {      o[this.name] = this.value || '';   }});return o;}//表单序列化://第一种: 序列化提交 $(’#form’).serialize()  //这种方式是将表单数据 格式化为  k=v&k=v&k=v&k=v//第二种:引入  jquery.serializeExtend-1.0.1.js     $('#form').getJsonData()//JSON.stringify()的作用是将 JavaScript 对象(json对象)转换为 JSON 字符串,//而JSON.parse()可以将JSON字符串转为一个JavaScript 对象(json对象)。$('#myForm').submit(function(e) {   var temp = $("#myForm").serializeObject();e.preventDefault(); // 阻止表单默认提交行为$.ajax({url: 'http://localhost:8080/reg2', // 假设表单有action属性type: 'post',contentType: 'application/json',dataType: 'json',data: JSON.stringify(temp) ,//$(this).serialize(): 是一些k-v字符串(serialize() 方法通过序列化表单值,创建 URL 编码文本字符串。)//JSON.stringify( $(this).serialize() )success: function(response) {// 成功回调函数console.log('Form submitted:', response);//alert("成功"+response.userName);alert("成功"+JSON.stringify(response));},error: function(xhr, status, error) {// 失败回调函数console.error('Submission failed:', status, error);}});});
</script>
</html>
5.3.编写TestController:
package com.zyq.validation.controller;
import com.zyq.validation.pojo.entity.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {@PostMapping(value = "/reg")public User test(@Validated User user){//index.html访问出来后提交数据就会报400的异常,提示名字字符过多,年龄超过限制return user;}//test2方法需要用postman或apifox 或者ajax的方式提交数据来访问(不能用html的form表单的action提交参数)//因为html的form表单的action提交方式的Content-type的值是'application/x-www-form-url (而且无法修改<我目前掌握的知识无法修改它>)//可以将form表单的数据改用ajax方式提交数据@PostMapping(value = "/reg2", produces = "application/json;charset=UTF-8")public User test2(@RequestBody @Validated User user){//index.html访问出来后提交数据就会报400的异常,提示名字字符过多,年龄超过限制return user;}
}
//@RequestBody注解解释:
//@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的,所以只能接收post方式数据);
//在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
//注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。
//注:当同时使用@RequestParam()和@RequestBody时,@RequestParam()指定的参数可以是普通元素、//数组、集合、对象等等(即:当,@RequestBody 与@RequestParam()可以同时使用时,原SpringMVC接收参数的机制不变,只不过RequestBody 接收的是请求体里面的数据;而RequestParam接收的是key-value
//里面的参数,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)。
//即:如果参数时放在请求体中,传入后台的话,那么后台要用@RequestBody才能接收到;如果不是放在//请求体中的话,那么后台接收前台传过来的参数时,要用@RequestParam来接收,或则形参前什么也不写也能接收。
//注:如果参数前写了@RequestParam(xxx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通
//过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。
//
//注:如果参数前不写@RequestParam(xxx)的话,那么就前端可以有可以没有对应的xxx名字才行,如果有xxx名
//的话,那么就会自动匹配;没有的话,请求也能正确发送。
5.4.浏览器测试:

执行结果:

六、传递校验:

在一个Customer顾客类中定义一个成员变量 User  user来引用用户类对象,并用@Valid注解进行属性验证。

6.1.Customer顾客类
package com.zyq.validation.pojo.entity;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
@Data
public class Customer  implements Serializable {@NotBlank(message = "会员卡不能为空")private String idCardNo;@Validprivate User user;
}

再比如 某个类中有一个成员变量为:         private  List<User>  users;     此时也可以给这个成员变量上边加@Valid 来校验users中的元素。

6.2.TestController

TestController控制器类中添加如下方法

@PostMapping(value = "/reg3", produces = "application/json;charset=UTF-8")
public Customer test3(@RequestBody @Validated Customer customer){return customer;
}//https://app.apifox.com/project/4524520
6.3.apifox进行测试

七、分组检测:
7.1.将User复制为User2

7.2.TestController加方法

7.3.测试:

分组1  group1的测试

分组2  group2的测试

八、自定义校验:
8.1.自定义注解:
package com.zyq.validation.annotation;
import com.zyq.validation.validator.CannotBlankValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;//此注解可用在方法上, 成员变量上, 注解上, 构造方法上, 方法参数上
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
//在运行时生效
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {//默认错误信息String  message() default "不能包含空格";//分组Class<?>[] groups() default {};//负载Class <? extends Payload>[]   payload() default{};//指定多个时使用@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documented@interface List {CannotBlank[] value();}
}
8.2.自定义校验器:
package com.zyq.validation.validator;
import com.zyq.validation.annotation.CannotBlank;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {public boolean isValid(String value, ConstraintValidatorContext context) {//null时不进行校验if(value != null  &&  value.contains(" ")){//如果value包含空格就不能通过验证。//获取默认提示信息String defaultConstraintMessgeTemplate=context.getDefaultConstraintMessageTemplate();System.out.println("default message :"+defaultConstraintMessgeTemplate);//禁用默认提示信息context.disableDefaultConstraintViolation();//设置提示语context.buildConstraintViolationWithTemplate("can not contains blank-不能包含空格").addConstraintViolation();return false;}return true;}
}
8.3.统一异常处理:

统一异常处理类中将exception方法复制一份儿,然后将异常类型改为BindException

8.4.Student:
package com.zyq.validation.pojo.entity;
import com.zyq.validation.annotation.CannotBlank;
import lombok.Data;
@Data
public class Student {@CannotBlank(message = "名字不能包含空格")private String stuName;
}
8.5.TestController:

8.6.apifox测试:

控制台显示:

九、让字段逐个校验:
9.1.写配置类:
package com.zyq.validation.configuration;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@Configuration
public class ValidatorConfiguration {//本配置类可以让需要校验的字段一个一个校验,而不是全部字段一次性都校验@Beanpublic Validator validator(AutowireCapableBeanFactory springBeanFactory){ValidatorFactory factory= Validation.byProvider(HibernateValidator.class).configure()//快速失败.failFast(true)//解决springboot依赖注入的问题.constraintValidatorFactory(new SpringConstraintValidatorFactory(springBeanFactory)).buildValidatorFactory();return factory.getValidator();}
}
9.2.配置文件

application.properties

9.3.测试:

localhost:8080/index2.html

十.依赖校验:

一个字段的校验依赖另一个字段,比如姓名:中国姓名:姓如果一个字或两个的话,名字基本是1-4个字之间。  

外国姓名::姓如果超过两字的话,名字基本是3-17个字之间

案例中的校验规则是(User3GroupSequenceProvider中指定):

姓如果一个字或两个的话,采用第一组校验规则(名字基本是1-4个字之间)

姓如果超过两字的话,采用第二组校验规则(名字基本是3-17个字之间)

10.1.删ValidatorConfiguration

10.2.校验分组:
package com.zyq.validation.configuration;
import java.util.ArrayList;
import java.util.List;
import com.zyq.validation.pojo.entity.User3;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
//本类用于:根据lastName判断firstName(firstName为0时lastName是一种校验方式,不为0时是另一种校验方式)
//如果account为空,密码也就不能有数据(用第一组校验规则)
//如果account不为空就判断密码字符个数(用第二组校验规则)
@Slf4j
public class User3GroupSequenceProvider implements DefaultGroupSequenceProvider<User3> {public List<Class<?>> getValidationGroups(User3 user3) {List<Class<?>>  defaultGroupSequence=new ArrayList<>();defaultGroupSequence.add(User3.class);if(user3!=null){String firstName=user3.getFirstName();log.info("账号{}.",firstName);if(firstName!=null&&1<=firstName.length() && firstName.length()<=2 )//如果姓的长度在1到3之间,其他字段可以采用分组1进行校验defaultGroupSequence.add(User3.group1.class);elsedefaultGroupSequence.add(User3.group2.class);/*String acc=user3.getAccount();log.info("账号{}",acc);if(acc==null||acc.equals(""))//如果用户名不存在,其他字段可以采用分组1进行校验defaultGroupSequence.add(User3.group1.class);elsedefaultGroupSequence.add(User3.group2.class);*/}return defaultGroupSequence;}
}
10.3.编写User3
package com.zyq.validation.pojo.entity;
import com.zyq.validation.configuration.User3GroupSequenceProvider;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.group.GroupSequenceProvider;
import javax.validation.constraints.*;
import java.util.Date;
@GroupSequenceProvider(User3GroupSequenceProvider.class)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User3 {  //根据lastName判断firstName(firstName为0时lastName是一种校验方式,不为0时是另一种校验方式)@NotNull(message = "firstName不能为null")@Size(min=1,max=15,message = "用户名字符数不符合规范(1到15个字符)") //SpringValidation框架提供的检测注解private String  firstName;//如果account不为空就判断密码字符个数(用第2组校验规则)@Size(min=1,max=3,message = "中国名字字符数不符合规范(1到3个字符)" , groups = {User3.group1.class})     
@Size(min=3,max=17,message = "英文名字字符数不符合规范(3到17个字符)" , groups = {User3.group2.class}) private String  lastName;//名public interface  group1{}//如果account为空,密码也就不能有数据(用第一组校验规则)public interface  group2{}//如果account不为空就判断密码字符个数(用第2组校验规则)
}
10.4TestController:
@RequestMapping(value = "/reg7", produces = "application/json;charset=UTF-8")
public User3 test7(@RequestBody @Validated User3 user){return user;
}//https://app.apifox.com/project/4524520
10.5.apifox测试:

A.英文姓,中文名:

控制台:

B.中文姓,英文名:

控制台信息:

11、自定义校验器2

11.1.注释ValidatorConfiguration:

注释掉ValidatorConfiguration中所有

11.2.编写User1:
package com.zyq.validation.pojo.entity;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User1 {@NotNull(message = "用户名不能为null") //SpringValidation框架提供的检测注解@NotBlank(message="名字不能为空") //SpringValidation框架提供的检测注解@CannotBlank(message = "名字不能包含空格")@Size(min=2,max=5,message = "名字字符过多(2到5个字符)") //SpringValidation框架提供的检测注解private String  userName;private int age;
}
11.3.AgeOutOfRangeException
package com.zyq.validation.exception;
import lombok.Data;
@Data
public class AgeOutOfRangeException extends  RuntimeException{private String key;private String val;public AgeOutOfRangeException(String message) {    super(message);       }public AgeOutOfRangeException(String message,String key,String val) {super(message);this.key=key;this.val=val;}public AgeOutOfRangeException(String message, Throwable cause) {   super(message, cause);     }public AgeOutOfRangeException(Throwable cause) {   super(cause);      }public AgeOutOfRangeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}
11.4.GlobalException

GlobalException统一异常处理类中添加如下方法:

@ExceptionHandler(AgeOutOfRangeException.class)//捕获所有controller的BindException异常
@ResponseBody
public ResponseEntity<Object> exception(AgeOutOfRangeException e, HttpServletRequest request) {Map<String, String> result = new HashMap<>();//request.getMethod():请求方式,request.getRequestURI() 请求路径 //比如:  post    /reg2log.error("请求[ {} ] {} 的参数校验发生错误", request.getMethod(), request.getRequestURL());log.error("参数 {} = {} 校验错误:{}", e.getKey(), e.getVal(), e.getMessage());result.put(e.getKey(), e.getMessage());return ResponseEntity.badRequest().body(result);
}
11.5.AgeBetweenValidator:
package com.zyq.validation.validator;
import com.zyq.validation.pojo.entity.User1;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;public class AgeBetweenValidator implements Validator{public boolean supports(Class<?> aClass) {//参数是否是User1的字节码对象return User1.class.equals(aClass);//判断参数aClass是否为User1.class}public void validate(Object objTarget, Errors errors) {ValidationUtils.rejectIfEmpty(errors,"age","age can not empty年龄不能为空");User1 u1=(User1)objTarget;if(0>=u1.getAge()){errors.rejectValue("age","age<=0, 年龄不能小于等于0");}else if( u1.getAge() >=239){errors.rejectValue("age","age>=239, 年龄不能大于等于239");}}}
11.6.TestController

TestController中添加如下 方法

@InitBinder
protected void initBinder(WebDataBinder binder) {binder.addValidators(new AgeBetweenValidator());
}@RequestMapping(value = "/reg1", produces = "application/json;charset=UTF-8")
public User1 test1(@RequestBody @Validated User1 user, BindingResult result){// 参数校验if (result.hasErrors()) {List<FieldError> fieldErrors = result.getFieldErrors();fieldErrors.forEach(e -> {System.out.println(e.getField() + e.getCode());});throw new AgeOutOfRangeException("年龄数值超范围异常[1,239]","age",user.getAge()+"");}return user;
}//https://app.apifox.com/project/4524520

统一异常处理类GlobalException中有两个统一异常处理方法, apifox进行测试如果账号和年龄都不能通过校验的话,则网页中只能接收到年龄的校验结果(接收不到用户名的校验结果), 原因应该就是统一异常处理类的两个方法的问题, 改进方案待以后研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/18098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序使用vant组件库

一:下载组件库 在小程序内npm下载的包 vant组件库官网:快速上手 - Vant Weapp (youzan.github.io) 1)首先有有package.json文件,没有的话则先初始化 即使通过package.json去下载包,也需要有,可以观察下载的包. 2)下载包 3)构建npm包 下载包之后存储在node_modules内,但是我们…

初学者必读:Midjourney AI创作工具的简易使用手册!

在数字化时代&#xff0c;AI的应用不断推动着各个领域的发展。在这些领域中&#xff0c;AI在艺术和设计方面的应用引起了广泛的关注。AI绘画软件作为今年的热门&#xff0c;Midjourney 通过其独特的原理和方便的使用方法&#xff0c;为创作者提供了一个全新的创作逼真绘画的平台…

大模型日报|今日必读的 5 篇大模型论文

大家好&#xff0c;今日必读的大模型论文来啦&#xff01; 1.Meta 领衔&#xff1a;一文读懂视觉语言建模&#xff08;VLM&#xff09; 人们正在尝试将大型语言模型&#xff08;LLMs&#xff09;扩展到视觉领域。从可以引导我们穿越陌生环境的视觉助手&#xff0c;到仅使用高…

富港银行 优惠链接 邀请码 兑换码 优惠码 分享

首次记得一定要点击链接注册&#xff0c;注册开户费50美金 限时&#xff01;优惠开通国际银行账户&#xff01; cbi帐户管理费&#xff1a;10美元/月&#xff0c;余额>500美元&#xff0c;1美元/月/&#xff0c;余额>2000美元&#xff0c;0美元/月。 一定要显示50的时候…

一分钟学习数据安全——数字身份的三种模式

微软首席身份架构师金卡梅隆曾说&#xff1a;互联网的构建缺少一个身份层。互联网的构建方式让你无法得知所连接的人和物是什么。这限制了我们对互联网的使用&#xff0c;并让我们面临越来越多的危险。如果我们坐视不管&#xff0c;将面临迅速激增的盗窃和欺诈事件&#xff0c;…

短视频内容创意方法有哪些?成都科成博通文化传媒公司

短视频内容创意方法有哪些&#xff1f; 随着移动互联网的迅猛发展&#xff0c;短视频平台已成为人们日常生活中不可或缺的一部分。短视频以其短平快的特点&#xff0c;迅速吸引了大量用户。然而&#xff0c;面对海量的短视频内容&#xff0c;如何让自己的作品脱颖而出&#xf…

web自动化-下拉框操作/键鼠操作/文件上传

在我们做UI自动化测试的时候&#xff0c;会有一些元素需要特殊操作&#xff0c;比如下拉框操作/键鼠操作/文件上传。 下拉框操作 在我们很多页面里有下拉框的选择&#xff0c;这种元素怎么定位呢&#xff1f;下拉框分为两种类型&#xff1a;我们分别针对这两种元素进行定位和…

Nocobase快速上手 -第一个collection

本文记录Nocobase中如何创建collection&#xff0c;以及如何将collection展示到页面中&#xff0c;并且配置CRUD相应的操作. Collection 在NocoBase中&#xff0c;collection&#xff08;集合&#xff09;是用来组织和存储各种数据的容器&#xff0c;如订单、产品、用户、评论…

汽车电子零部件(14):TMS热管理系统

前言: TMS(thermal management system)热管理系统,这是新能源汽车诞生后随之而产生的一种新汽车零部件,一旦热管理失控会触发自燃,这种现象也是对EV来说是件头疼的事。汽车的热管理系统(TMS)是一个关键部件,有助于调节汽车电池组、车厢和其他车辆系统的温度。TMS的主要…

M功能-分布式锁-支付平台(五)

target&#xff1a;离开柬埔寨倒计时-218day 珍藏的图片又拿出来了 前言 M系统中的撮合引擎是最最核心的功能&#xff0c;第一版的撮合引擎不是我写的&#xff0c;也没有做交易对的动态分配这样的功能&#xff0c;都是基于抢锁方式来决定谁拥有该交易对的撮合权限&#xff0c;所…

MySQL--复合查询

之前学过了基本的查询&#xff0c;虽然已经够80%的使用场景了&#xff0c;但是依旧需要了解剩下的20%。 一、多表笛卡尔积&#xff08;多表查询&#xff09; 以前我们使用基本查询的时候&#xff0c;from后面就跟一张表名&#xff0c;在多表查询这里&#xff0c;from后面可以跟…

13 VUE学习:组件v-model

基本用法 v-model 可以在组件上使用以实现双向绑定。 从 Vue 3.4 开始&#xff0c;推荐的实现方式是使用 [defineModel()]宏&#xff1a; <!-- Child.vue --> <script setup> const model defineModel()function update() {model.value } </script><te…

GitLab的安装及基础操作

1. 项目目标 &#xff08;1&#xff09;熟练使用rpm包安装gitlab &#xff08;2&#xff09;熟练配置gitlab &#xff08;3&#xff09;熟练创建gitlab群组、成员、项目 &#xff08;4&#xff09;熟练使用gitlab推送和拉取代码 2. 项目准备 2.1. 规划节点 主机名 主机I…

Gb 2024-05-22开源项目日报Top10

根据Github Trendings的统计,今日(2024-05-22统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目3非开发语言项目2Jupyter Notebook项目2Rust项目2JavaScript项目1Lua项目1编程面试大学:成为软件工程师的全面学习计划 创建周期:2…

查询DQL

016条件查询之等量关系 条件查询语法格式 select ... from... where过滤条件;等于 select empno, ename from emp where sal3000;select job, sal from emp where enameFORD;select grade, losal, hisal from salgrade where grade 1;不等于 <> 或 ! selectempno,en…

德比软件携手亚马逊云科技,用生成式AI赋能旅游行业降本增效

旅游行业是最早被数字化技术赋能的行业之一。比如&#xff0c;消费者早已习惯在携程、艺龙、Booking等OTA平台根据实时酒店信息预订酒店。 这种丝滑的消费者体验背后&#xff0c;离不开领先的管理软件支撑。实际上大型酒店集团与OTA平台之间的系统对接非常复杂&#xff0c;酒店…

Go GORM介绍

GORM 是一个功能强大的 Go 语言 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它提供了一种方便的方式来与 SQL 数据库进行交互&#xff0c;而不需要编写大量的 SQL 代码。 GORM的关键特性 全功能的ORM&#xff1a;支持几乎所有的ORM功能&#xff0c;包括模型定义、基…

在Ubuntu系统中使用Systemctl添加启动项的详细指南

在Ubuntu系统中使用Systemctl添加启动项的详细指南 在Ubuntu系统中&#xff0c;systemctl 是管理systemd服务的主要工具。通过它&#xff0c;你可以添加、启动、停止、重启、启用和禁用服务。 什么是Systemctl&#xff1f; systemctl 是一个用于管理systemd系统和服务管理器…

OpenHarmony迎来首个互联网技术统一标准,鸿蒙OS生态走向如何?

开源三年半&#xff0c;OpenHarmony(以下简称“开源鸿蒙”)迎来了新进展。在5月25日召开的「OpenHarmony开发者大会」上&#xff0c;鸿蒙官宣了开源鸿蒙设备统一互联技术标准。 一直以来&#xff0c;各行业品牌操作系统相互独立、难以协同,成为其互联互通的痛点。为进一步解决…