SpringBoot参数校验的最佳实战教程
作者:范闲 发布时间:2023-11-28 21:13:47
目录
前言
hibernate-validator基本使用
引入依赖
编写需要验证对象
验证对象属性是否符合要求
验证规则
空/非空验证
bool
时间
数学
字符串
模板正则
SpringBoot整合hibernate-validator
引入依赖
配置hibernate-validator验证器对象
借助SpringMVC统一异常处理处理参数校验结果
使用参数校验
分组校验
啥叫分组校验?
总结
前言
我们这里使用hibernate-validator作为对象参数验证器,所以在正式介绍SpringBoot参数验证之前,需要先简单了解一下hibernate-validator的使用。
hibernate-validator基本使用
引入依赖
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
编写需要验证对象
验证要求 person对象的用户名不能为空,年龄在1-150岁之间。
@Data
public class Person {
@NotBlank(message = "username must not be null")
private String username;
@Min(value = 1, message = "age must be >= 1")
@Max(value = 150, message = "age must be < 150")
private Integer age;
}
验证对象属性是否符合要求
/**
* 对象验证器
*/
public Validator validator() {
ValidatorFactory validatorFactory =
Validation
.byProvider(HibernateValidator.class)
.configure()
// 验证属性时,如果有一个验证不通过就返回,不需要验证所有属性
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
@Test
public void test() throws Exception {
Person person = new Person();
Set<ConstraintViolation<Person>> validate = validator().validate(person);
validate.forEach(errorParam -> {
System.out.println(errorParam.getMessage());
});
}
我们只需要验证的对象实例即可完成对象验证,如果验证成功,那么返回一个空的集合,如果验证失败,会返回具体的验证失败的属性信息。
我们输出验证失败的错误信息如下:
username must not be null
验证规则
validator提供了大量的验证注解供我们使用,主要以下几类:
空/非空验证
@Null 元素必须为空
@NotNull 元素不能为空,空字符串""是非空
以下所有验证规则都在元素非空的时候才会进行验证,如果传入的元素为空,验证都会通过。
bool
@AssertTrue 元素必须为true
@AssertFalse 元素必须为false
时间
@Future 元素必须是未来的某个时间。
@FutureOrPresent 元素必须是未来或者现在的某个时间。
@Past 元素必须是过去的某个时间。
@PastOrPresent 元素必须是过去或者现在的某个时间。
数学
数字类型可以是BigDecimal、BigInteger、CharSequence 、byte 、 short 、 int 、 long以及它们各自的包装器类型
@Digits 元素必须是该数字类型下可以被接受的数值范围内。
@Negative 元素必须是负数
@NegativeOrZero 元素必须小于等于0
@Positive 元素必须大于0
@PositiveOrZero 元素必须大于等于0
@Max,@Min 元素的大小必须符合指定大小
字符串
@Email 邮箱格式验证
@NotBlack 验证字符串非空,空字符串""也属于空
@Pattern 字符串正则验证
模板正则
validator提供了字符串模板正则的注解,这里提供一份常用的正则表达式,大家可以直接作为常量工具类放到项目里使用
public interface ValidatorPattern {
/**
* 正则表达式:验证用户名
* 1.长度在5-17
* 2.由大写小写字母构成
*/
String REGEX_USERNAME = "^[a-zA-Z]\w{5,17}$";
/**
* 正则表达式:验证密码
* 密码只能为 6 - 12位数字,字母及常用符号组成。
*/
String REGEX_PASSWORD = "^(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9._~!@#$^&*]{6,12}$";
/**
* 正则表达式:验证手机号
*/
String REGEX_MOBILE = "^[1][34578]\d{9}$";
/**
* 正则表达式:验证邮箱
*/
String REGEX_EMAIL = "^.+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-zA-Z]{2,}$";
/**
* 正则表达式:验证汉字
*/
String REGEX_CHINESE = "^[\u4e00-\u9fa5],*$";
/**
* 正则表达式:验证身份证
*/
String REGEX_ID_CARD = "(^\d{18}$)|(^\d{15}$)";
/**
* 正则表达式:验证URL
*/
String REGEX_URL = "http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";
/**
* 正则表达式:验证IP地址
*/
String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)";
/**
* 车牌号正则
*/
String LICENSE_NO = "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4,5}[A-Z0-9挂学警港澳]$";
/**
* 姓名校验
* 1~15位
* 姓名支持空格和中文的点
*/
String NAME = "[\u4e00-\u9fa5\u00b7\sA-Za-z]{1,15}$";
/**
* 表情正则
*/
String EMOJI = "[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]";
/**
* 数字正则
*/
String NUMBER = "^[0-9]*$";
/**
* n位的数字
*/
String N_NUMS = "^\d{n}$";
}
SpringBoot整合hibernate-validator
引入依赖
这个不再赘述,直接拷贝上文的依赖信息
配置hibernate-validator验证器对象
在配置类中加入hibernate-validator验证器对象
@Bean
@Primary
public Validator validator() {
ValidatorFactory validatorFactory =
Validation
.byProvider(HibernateValidator.class)
.configure()
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
借助SpringMVC统一异常处理处理参数校验结果
配置好后,Spring会自动帮助我们进行参数验证,如果参数验证不通过,会抛出BindException异常,我们刚刚手动验证时的Set<ConstraintViolation<Person>>通过该异常获取。
我们这可以通过借助SpringMVC统一异常处理的能力处理这个异常
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {
/**
* spring validation 自动校验的参数异常
*
* @param e BindException
* @return R<Void>
*/
@ResponseStatus(org.springframework.http.HttpStatus.PAYMENT_REQUIRED)
@ExceptionHandler(BindException.class)
public R<Void> handler(BindException e) {
String defaultMsg = e.getBindingResult().getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining(":"));
log.warn(defaultMsg);
return R.of(IRespCode.PARAMETERS_ANOMALIES.getCode(), e.getMessage());
}
}
使用参数校验
我们只需要在校验参数的方法传参上标注@Valid或者@Validated都行
@PostMapping("register")
public R<Void> register(@Valid @RequestBody Person person) {
// todo
return R.ok();
}
分组校验
那么@Valid和@Validated有什么区别呢?
Validated比Valid多了一个属性,这个属性用于分组校验使用
public @interface Valid {
}
public @interface Validated {
Class<?>[] value() default {};
}
啥叫分组校验?
就是一个实体类中的属性,在不同的方法传参中,方法的对属性的要求不同。
比如说,Person类中有三个属性,一个是用户名称,一个是邮箱,一个是年龄。
在注册用户接口中,用户名称,邮箱和年龄都不能为空,但是在更改用户的信息接口中,用户的年龄和邮箱都可以为空,但是用户名称不能为空。
这时候,我们就可以按照对属性校验的要求进行分组。
新建一个RegisterGroup分组,该分组只是一个空的接口,仅仅用于标记该校验要求
public interface RegisterGroup {
}
对校验要求进行分组
@Data
public class Person {
@NotBlank(message = "username must not be null")
private String username;
@Min(value = 1, message = "age must be >= 1")
@Max(value = 150, message = "age must be < 150")
@NotNull(message = "age must not be null", groups = RegisterGroup.class)
private Integer age;
@Email(message = "email format error")
@NotBlank(message = "email must not be null",groups = RegisterGroup.class)
private String email;
}
方法调用时,加入分组要求
@PostMapping("register")
public R<Void> register(@Validated(value = RegisterGroup.class) @RequestBody Person person) {
// todo
return R.ok();
}
这种方式其实不推荐使用,我在标题的时候,也已经标记为“过时”,因为,我们完全可以为这两个不同的接口创建两个不同的实体类,而不是使用分组对校验要求进行隔离,因为实际生产环境中,分组可能有非常多个,这会为我们的程序的可读性埋下隐患,后期开发人员难以维护,而且对于自动生成API文档也不友好。大家对于分组只需要了解即可,不建议在项目开发中使用。
来源:https://juejin.cn/post/6999607085998538759


猜你喜欢
- 咱们废话不多说进入主题、系统主页展示:用户登录后进行系统首页:主要功能模块如下、分角色管理、超级管理员拥有最高权限、可以进行菜单灵活控制、用
- 在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。为什么选择 Kotlin? 简洁:
- 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。比如说,你的代码少了一个分号,那么运行出来结果是提示是错误
- 本文实例讲述了WPF中的ListBox实现按块显示元素的方法。分享给大家供大家参考,具体如下:注意:需要设置ListBox的属性 Scrol
- 网络办公正逐渐成为常态,无纸化办公也是一个潮流,这二者需要电子签章,最简单的方法就是在纸上盖一个章然后扫描成电子图片文件,最后在你的系统加载
- Service翻译成中文是服务,熟悉Windows 系统的同学一定很熟悉了。Android里的Service跟Windows里的Servic
- 向量向量是序列容器,表示可以更改大小的数组。就像数组一样,向量对其元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针上的偏移量来
- 汇总:1. 冒泡排序每轮循环确定最值;public void bubbleSort(int[] nums){ in
- 1.wait()方法和sleep()方法:wait()方法在等待中释放锁;sleep()在等待的时候不会释放锁,抱着锁睡眠。2.notify
- 1、synchronized的作用为了避免临界区的竞态条件发生,有多种手段可以达到目的。阻塞式的解决方案:synchronized,Lock
- 1.异步委托开启线程public class Program { public static void
- 先看下效果图:这个需要用到1个开源的 库,这个后面也会说下的。工程目录:1. MainActivity.javapublic class M
- 概述泛型机制在项目中一直都在使用,比如在集合中ArrayList<String, String>, Map<String,
- BroadcastReceiver(广播 * ),在Android开发中,BroadcastReceiver的应用场景非常多,属于Andro
- 本文实例为大家分享了C语言实现通讯管理系统的具体代码,供大家参考,具体内容如下#include<stdio.h>#include
- 数组是一种数据结构,其声明方式如下:type[] arrayName;数组具有以下属性: 1.数组可以是
- 本文实例为大家分享了java实现微信红包的具体代码,供大家参考,具体内容如下要求基于BigDecimal类实现微信红包算法的功能,比如设置红
- 简介从 Spring Boot 项目名称中的 Boot 可以看出来,Spring Boot 的作用在于创建和启动新的基于 Spring 框架
- 本文实例讲解了iOS从背景图中取色的代码,分享给大家供大家参考,具体内容如下实现代码:void *bitmapData; //内存空间的指针
- 使用过Mybatis的同学,应该都知道,我们只需要编写mybatis对应的接口和mapper XML文件即可,并不需要手动编写mapper接