在真实的开发中,我们经常会遇到需要对数据进行校验的业务,那么本篇文章对此进行总结。暂时总结三种方法,大家可以根据需要选择使用。
一、Java Bean Validation 验证
二、SpringBoot Validate 统一处理
三、Spring Validation 校验处理
一、Java Bean Validation 验证
1.引入依赖
注意:java bean validation参数验证,在使用的时候,有时候会报错,原因是跟spring版本不兼容,这里演示的版本如下:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.1.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2.测试实体类
注意:将需要的校验规则直接通过注解写在实体类对应字段上即可
@Data
@AllArgsConstructor
public class Test {
@Length(min = 1,max = 5,message = "姓名长度应该在1-5之间")
private String name;
@Range(min = 1,max = 100,message = "年龄应该在1-100之间")
private Integer age;
@DecimalMax(value = "100.00",message = "体重有些超标哦")
@DecimalMin(value = "60.00",message = "多吃点饭吧")
private BigDecimal weight;
@Future(message = "元素必须是一个将来的日期")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date date;
@AssertTrue(message = "此值只能为true")
private Boolean isuser;
@Email(message = "邮箱地址无效")
private String email;
}
3.校验工具类
注意:
(1)如果想要使用,直接调用ValidationUtils.validate参数直接传实体对象即可。
(2)validate方法可以优化,常见的是新建一个set集合,将所有的提示消息放到set中之后返回即可。
public class ValidationUtils implements Serializable {
public static void main(String[] args) {
Test test = new Test("张三张三张三",0,new BigDecimal(110.00),new Date(),false,"12345");
validate(test);
}
private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
public static <T> String validate(T t) {
try {
Validator validator = factory.getValidator();
Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
for (ConstraintViolation<T> constraintViolation : constraintViolations) {
System.out.println("invalidvalue: "+constraintViolation.getInvalidValue() == null?"":constraintViolation.getInvalidValue().toString()+" propertypath: "+constraintViolation.getPropertyPath()+" message: "+constraintViolation.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
4.总结一波实体类上支持的注解
二、SpringBoot Validate 统一处理
1. 原理
通过继承`ResponseEntityExceptionHandler`这个类并实现`handleMethodArgumentNotValid`这个方法,就可以统一处理所以经过`Valid`注解过的接口
源码地址:Github:https://github.com/lengrongfu/validate-common-demo
1.引入依赖
<!-- 注意:里面自带了需要的校验包hibernate-validator等 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.测试实体类
@Data
public class Test {
public Test(){}
@Length(min = 1,max = 5,message = "姓名长度应该在1-5之间")
private String name;
@Range(min = 1,max = 100,message = "年龄应该在1-100之间")
private Integer age;
@DecimalMax(value = "100.00",message = "体重有些超标哦")
@DecimalMin(value = "60.00",message = "多吃点饭吧")
private BigDecimal weight;
@Future(message = "元素必须是一个将来的日期")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date date;
@AssertTrue(message = "此值只能为true")
private Boolean isuser;
@Email(message = "邮箱地址无效")
private String email;
}
3.配置类
public class ValidateCommonHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status,WebRequest request) {
for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
String defaultMessage = fieldError.getDefaultMessage();
Object value = fieldError.getRejectedValue();
String data = "请求参数值为:"+ JSON.toJSONString(value);
break;
}
return new ResponseEntity("", HttpStatus.OK);
}
}
4.测试类
@ApiOperation("测试")
@PostMapping("/test")
public R test(@RequestBody @Valid Test test){
return R.ok();
}
//参数Test内容如下:
{
"name":"张三张三张三",
"age":"0",
"weight":"110.00",
"date":"2020-1-1 12:11:11",
"isuser":"false",
"email":"12345"
}
效果如下:
5.支持注解总结
6.一些常见的应用总结
下面定义一个实体类,来大概描述每种注解的常见用法:(涉及到嵌套校验)
@Data
public class Student {
@Length(min = 1,message = "姓名不能为空")
@NotNull(message = "姓名不能为空")
private String name;
@NotNull(message = "年龄不能为空")
@Min(value = 18,message = "年龄不能小于18岁")
@Max(value = 56,message = "年龄不能大于56岁")
private Integer age;
@NotNull(message = "邮箱不能为空")
@Email(message = "邮箱地址无效")
private String email;
@NotEmpty(message = "身份证号不能为空")
@Pattern(regexp = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$",message = "请输入正确的身份证号")
private String idCard;
@NotEmpty(message = "手机号码不能为空")
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "请输入正确的手机号码")
private String phone;
@NotNull(message = "请输入合法的时间")
@Future(message = "请输入合法的时间")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date inSchoolDate;
@NotNull(message = "请输入正确的银行卡号")
@CreditCardNumber(message = "请输入正确的银行卡号")
private String backCard;
//嵌套校验
@NotEmpty(message = "请填写女朋友信息")
private List<@Valid Girlfriend> girlfriends;
}
@Data
public class Girlfriend {
@NotEmpty(message = "请输入姓名")
private String name;
@NotNull(message = "请输入年龄")
@Range(min = 18,max = 24,message = "对不起,年龄不在合适的范围内")
private Integer age;
@NotNull(message = "请输入性别")
@Digits(integer = 1,fraction = 0,message = "只接受女性为女朋友")
private Integer gender;
}
三、Spring Validation 校验处理 *
1.背景
Java API规范(JSR303)定义了Bean校验的标准validation-api,但没有提供实现.
hibernate validation是对这个规范的实现,并增加了校验注解如@Email、@Length等.
Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数自动校验.
POST、PUT请求一般会使用requestBody传递参数,这种情况下,后端使用DTO对象进行接收.
只要给DTO对象加上@Validated注解就能实现自动参数校验.
如果校验失败,会抛出MethodArgumentNotValidException异常,Spring默认会将其转为400(Bad Request)请求.
DTO表示数据传输对象(Data Transfer Object),用于服务器和客户端之间交互传输使用的.
在spring-web项目中可以表示用于接收请求参数的Bean对象。
2.依赖
<!--引入依赖-->
<!--如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。-->
<!--如果spring-boot版本大于2.3.x,则需要手动引入依赖 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.1.Final</version>
</dependency>
3.校验处理
(1)简单校验
此处的校验注解写成Validated或者Valid都可以,不过他俩之间有一些区别:
1.分组:
@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制,这个网上也有资料,不详述。
@Valid:作为标准JSR-303规范,还没有吸收分组的功能。
2.注解位置:
@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
3.嵌套验证:
@Valid //嵌套验证必须使用@Valid
private List< Student> student;
4.@Valid一般用于方法入口,@Validated用于校验实体类的接口参数,用于controller上
@Data
public class Test {
@Length(min = 1,max = 5,message = "姓名长度应该在1-5之间")
private String name;
@Range(min = 1,max = 100,message = "年龄应该在1-100之间")
private Integer age;
}
@ApiOperation("测试")
@PostMapping("/test")
public R test(@RequestBody @Valid Test test){
return R.ok();
}
@ApiOperation("测试")
@PostMapping("/test")
public R test(@RequestBody @Validated Test test){
return R.ok();
}
{
"name":"张三张三张三",
"age":"0"
}
效果
(2)嵌套校验
嵌套校验一定要在要校验的对象类型的属性/list上使用@Valid注解:
@Data
public class Test {
@Length(min = 1,max = 5,message = "姓名长度应该在1-5之间")
private String name;
@Range(min = 1,max = 100,message = "年龄应该在1-100之间")
private Integer age;
@Valid
private Test2 test;
@Valid
private List<Test2> test2;
}
@Data
public class Test2 {
@Max(value = 1)
private Integer sex;
@Length(min = 0,max = 2)
private String jobname;
}
@ApiOperation("测试")
@PostMapping("/test")
public R test(@RequestBody @Validated Test test){
return R.ok();
}
{
"name":"张三张三张三",
"age":"0",
"test":{
"sex":"11",
"jobname":"李四李四李四"
},
"test2":[{
"sex":"222",
"jobname":"王五王五王五王五"
},{
"sex":"3333",
"jobname":"赵六赵六赵六赵六"
}]
}
效果
(3)分组校验
在实际项目中,可能多个方法需要使用同一个DTO类来接收参数,而不同方法的校验规则很可能是不一样的。
这个时候,简单地在DTO类的字段上加约束注解无法解决这个问题。因此,spring-validation支持了分组校验的功能,专门用来解决这类问题。
//这里定义四种规则
public class GroupValidator {
public interface Arules{}
public interface Brules{}
public interface Crules{}
public interface Drules{}
}
//实体类属性注解上加上分组标志
@Data
public class Test {
@Length(min = 1,max = 5,groups = {GroupValidator.Arules.class, GroupValidator.Brules.class})
private String name;
@Range(min = 1,max = 100,groups = {GroupValidator.Crules.class, GroupValidator.Drules.class})
private Integer age;
}
//参数列表里指定要校验的分组
@PostMapping("/test")
public R test(@RequestBody @Validated(GroupValidator.Arules.class) Test test){
return R.ok();
}
//json
{
"name":"张三张三张三",
"age":"0"
}
效果
(4)自定义校验
//首先自定义一个注解
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {TestValidator.class})
public @interface TestAnnotation {
// 默认错误消息
String message() default "此值只能为1";
// 分组
Class<?>[] groups() default {};
// 负载
Class<? extends Payload>[] payload() default {};
}
//对这个注解写验证的逻辑
public class TestValidator implements ConstraintValidator<TestAnnotation, String> {
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if(!"1".equals(s)){
return false;
}else{
return true;
}
}
}
//实体类中使用这个注解
@Data
public class Test {
//@Length(min = 1,max = 5)
@TestAnnotation
private String name;
@Range(min = 1,max = 100)
private Integer age;
}
//验证
@PostMapping("/test")
public R test(@RequestBody @Validated Test test){
return R.ok();
}
//传入json
{
"name":"张三张三张三",
"age":"0"
}
效果
参考
1.www.cnblogs.com/xiaogangfan… 2.cloud.tencent.com/developer/a… 3.lengrongfu.github.io/2018/12/27/…