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.rhkb.cn/news/333023.html

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

相关文章

一文教你如何调用Ascend C算子

Ascend C是CANN针对算子开发场景推出的编程语言&#xff0c;原生支持C和C标准规范&#xff0c;兼具开发效率和运行性能。基于Ascend C编写的算子程序&#xff0c;通过编译器编译和运行时调度&#xff0c;运行在昇腾AI处理器上。使用Ascend C&#xff0c;开发者可以基于昇腾AI硬…

AC/DC电源模块:提供高质量的电力转换解决方案

BOSHIDA AC/DC电源模块&#xff1a;提供高质量的电力转换解决方案 AC/DC电源模块是一种电力转换器件&#xff0c;可以将交流电转换为直流电。它通常用于各种电子设备和系统中&#xff0c;提供高质量的电力转换解决方案。 AC/DC电源模块具有许多优点。首先&#xff0c;它能够提…

【学习笔记】计算机组成原理(七)

指令系统 文章目录 指令系统7.1 机器指令7.1.1 指令的一般格式7.1.2 指令字长 7.2 操作数类型和操作类型7.2.1 操作数类型7.2.2 数据在存储器中的存放方式7.2.3 操作类型 7.3 寻址方式7.3.1 指令寻址7.3.1.1 顺序寻址7.3.1.2 跳跃寻址 7.3.2 数据寻址7.3.2.1 立即寻址7.3.2.2 直…

探秘SpringBoot默认线程池:了解其运行原理与工作方式(@Async和ThreadPoolTaskExecutor)

文章目录 文章导图Spring封装的几种线程池SpringBoot默认线程池TaskExecutionAutoConfiguration&#xff08;SpringBoot 2.1后&#xff09;主要作用优势使用场景如果没有它 2.1版本以后如何查看参数方式一&#xff1a;通过Async注解--采用ThreadPoolTaskExecutordetermineAsync…

Samtec技术漫谈 | 电动自行车中的传感器和信号传输技术

【摘要/前言】 电动自行车&#xff0c;大家熟悉吗&#xff1f; 今天的话题似乎是可以唤起大家心底骑车的美好回忆&#xff0c;我们也曾骑车探索过大自然和社区&#xff0c;自行车也是我们曾经不可或缺的便捷交通工具。 怀旧思潮的影响&#xff0c;加持科技的进步&#xff0c…

Flask 蓝图路由的模块化开发

基于 Flask 蓝图路由的模块化开发 1. 编程目标 为了提高Flask应用的可维护性和可扩展性&#xff0c;我们通过使用Flask的蓝图(Blueprint)功能&#xff0c;可以将不同的功能模块拆分到独立的文件中&#xff0c;方便后续的开发和维护。 2. 项目结构 项目结构树如下&#xff1…

Uni-App开发 导入(引入)Vant-Weapp组件;支持vue3/vue2版本和微信小程序

文章目录 目录 文章目录 操作流程 小结 概要安装流程技术细节小结 概要 Vant Weapp官网&#xff1a;Vant Weapp - 轻量、可靠的小程序 UI 组件库 准备工作&#xff0c;需要确保自己的电脑上已安装Hbuilde和node 全程操作的环境都需要这些配合才能运行上&#xff0c;可参考作者…

M功能-支付平台(三)

target&#xff1a;离开柬埔寨倒计时-221day 前言 今天周六&#xff0c;但是在柬埔寨还是工作日&#xff0c;想着国内的朋友开始休周末就羡慕呀&#xff0c;记不清在这边过了多少个周六了&#xff0c;多到我已经习惯了。而且今天技术部还停电了&#xff0c;真的是热的受不了呀…

【MATLAB源码-第74期】基于matlab的OFDM-IM索引调制系统不同频偏误码率对比,对比OFDM系统。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM-IM索引调制技术是一种新型的无线通信技术&#xff0c;它将正交频分复用&#xff08;OFDM&#xff09;和索引调制&#xff08;IM&#xff09;相结合&#xff0c;以提高频谱效率和系统容量。OFDM-IM索引调制技术的基本思想…

Android 性能为王时代SparseArray和HashMap一争高下

文章目录 一、SparseArray 源码分析1. **类定义和构造函数**2. **基本方法**2.1 put(int key, E value)2.2 get(int key)2.3 delete(int key)2.4 removeAt(int index)2.5 gc()2.6 size()2.7 keyAt(int index) 和 valueAt(int index) 3. **辅助方法**3.1 binarySearch() 二、使用…

【SpringCloud】负载均衡

目录 负载均衡什么是负载均衡生活场景为什么需要负载均衡负载均衡手段负载均衡总的来说有两种实现手段负载均衡具体可以通过多种手段来实现 SpringCloud中的负载均衡组件Ribbon VS Nginx负载均衡区别集中式LB进程内LB RibbonRibbon的工作原理Ribbon在工作时分成两步 使用1.提供…

昔日辉煌不再,PHP老矣,尚能饭否?

导语 | 近期 TIOBE 最新指数显示&#xff0c;PHP 的流行度降至了历史最低&#xff0c;排在第 17 名&#xff0c;同时&#xff0c;在年度 Stack Overflow 开发者调查报告中&#xff0c;PHP 在开发者中的受欢迎程度已经从之前的约 30% 萎缩至现在的 18%。“PHP 是世界上最好的语言…

【开源】大学生竞赛管理系统 JAVA+Vue+SpringBoot+MySQL

目录 一、系统介绍 学生管理模块 教师管理模块 竞赛信息模块 竞赛报名模块 二、系统截图 三、核心代码 一、系统介绍 基于Vue.js和SpringBoot的大学生竞赛管理系统&#xff0c;分为管理后台和用户网页端&#xff0c;可以给管理员、学生和教师角色使用&#xff0c;包括学…

【Flutter】Dialog组件PageView组件

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Flutter学习 &#x1f320; 首发时间&#xff1a;2024年5月27日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; 目…

如何确保大模型 RAG 生成的信息是基于可靠的数据源?

在不断发展的人工智能 (AI) 领域中&#xff0c;检索增强生成 (RAG) 已成为一种强大的技术。 RAG 弥合了大型语言模型 (LLM) 与外部知识源之间的差距&#xff0c;使 AI 系统能够提供更全面和信息丰富的响应。然而&#xff0c;一个关键因素有时会缺失——透明性。 我们如何能够…

mysql中单表查询的成本

大家好。我们知道MySQL在执行一个查询时&#xff0c;经常会有多个执行方案&#xff0c;然后从中选取成本最低或者说代价最低的方案去真正的执行查询。今天我们来聊一聊单表查询的成本。 那么到底什么是成本呢&#xff1f;这里我们说的成本或者代价是由两方面组成的&#xff1a…

vscode插件-03 PHP

PHP Intelephense 如果php在远程计算机上&#xff0c;要把插件安装在远程&#xff0c;而不是本地。 这个插件&#xff0c;要求php版本大于7&#xff0c;且设置环境变量&#xff08;好像不一定要设置&#xff09;。 设置里面搜索php.executablePath&#xff0c;打开setting.js…

element ui 的el-input输入一个字后失去焦点,需重新点击输入框才能再次输入

解决方案&#xff1a; 我是form表单嵌套表格&#xff0c;里面的el-input输入框&#xff0c;输入第一个值的时候会突然失去焦点&#xff0c;需要再次点击输入框才能正常输入&#xff0c;原因是table的key值&#xff0c;需要改成正常的index即可&#xff0c;如果你是循环的&…

ESP32入门:1、VSCode+PlatformIO环境搭建

文章目录 背景安装vscode安装配置中文 安装Platform IO安装PIO 新建ESP32工程参考 背景 对于刚接触单片机的同学&#xff0c;使用vscodeplatformIO来学习ESP32是最方便快捷的&#xff0c;比IDF框架简单&#xff0c;且比arduino文件管理性能更好。但是platformIO安装较为麻烦&a…

gnocchi学习小结

背景 总结gnocchi 4.4版本gnocchi-metricd工作流程 入口 gnocchi.cli.metricd metricd stop after processing metric默认为0&#xff0c;调servicemanager run MetricdServiceManager __init__ 服务逻辑封装到MetricdServiceManager初始化中 主要由MetricProcessor, Met…