一、引入依赖
===============================================================================================
```xml
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!--springboot 新版本需要validation启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
```
1. 验证注解都在 `javax.validation.constraints` 这个包下
2. 默认返回的异常信息存放在 `ValidationMessages_zh_CN.properties` 文件中
二、基本校验
===============================================================================================
1\. 常用校验注解
---------------------------------------------------------------------------------------------------
| 注解 | 类型 | 说明 |
| :-- | :-- | :-- |
| @Null | Object | 被注释的元素必须为 null |
| @NotNull | Object | 被注释的元素必须不为 null |
| @AssertTrue | boolean/Boolean | 被注释的元素必须为 true |
| @AssertFalse | boolean/Boolean | 被注释的元素必须为 false |
| @Min(value) | BigDecimal/BigInteger/byte/short/int/long | 被注释数字必须大于等于指定的最小值 |
| @Max(value) | BigDecimal/BigInteger/byte/short/int/long | 被注释数字必须小于等于指定的最大值 |
| @Range(min=,max=) | BigDecimal/BigInteger/byte/short/int/long | @Min 和 @Max 注解的合并 |
| @DecimalMin(value) | BigDecimal/BigInteger/byte/short/int/long | 被注释的数字必须大于等于指定的最小值 |
| @DecimalMax(value) | BigDecimal/BigInteger/byte/short/int/long | 被注释的数字必须小于等于指定的最大值 |
| @Size(max=, min=) | CharSequence/Collection/Map/Array | 被注释的集合、字符串的长度必须在指定的范围内 |
| @Digits (integer, fraction) | BigDecimal/BigInteger/byte/short/int/long | 被注释的元素必须是一个数字,且满足 integer 参数表示整数位数最大值,fraction 表示小数位数的最大值 |
| @Past | Date/Calendar | 被注释的日期必须是一个过去的日期 |
| @Future | Date/Calendar | 被注释的日期必须是一个将来的日期 |
| @Pattern(regex=,flag=) | String | 被注释的字符串必须符合指定的正则表达式 |
| @NotBlank | String | 验证字符串非null,且长度必须大于0 |
| @Length(min=,max=) | String | 被注释的字符串的长度必须在指定的范围内 |
| @NotEmpty | string/collection/map/array | 被注释的字符串或集合不为空 |
| @Email | String | 被注释的字符串必须是电子邮箱地址 |
2\. 自动校验
-------------------------------------------------------------------------------------------------
1)定义一个实体类
```java
public class Student {
@Min(1)
private Integer id;
@NotBlank
private String name;
@Range(min = 0, max = 200)
private Integer age;
@Min(1)
@Digits(fraction = 10, integer = 2)
private BigDecimal money;
}
```
2)在请求时加上注解 @Valid
```java
@PostMapping(value = "/createStudent")
public R save(@Valid @RequestBody Student student){
}
```
3\. 代码中获取校验结果
------------------------------------------------------------------------------------------------------
加上 `@valid` 后台验证,在验证的参数后加 `BindingResult` 就可以获取验证结果,不获取结果会抛出 `MethodArgumentNotValidException` 异常
```java
@PostMapping(value = "/createStudent")
public R save(@Valid @RequestBody Student student, BindingResult result){
if (result.hasErrors()) {
Map<String , String > map = new HashMap<>();
result.getFieldErrors().forEach(item ->{
String msg = item.getDefaultMessage();
String field = item.getField();
map.put(field, msg);
});
return R.error(400, "提交的数据不合法").put("data", map);
}
studentService.save(student);
return R.ok();
}
```
三、嵌套校验
===============================================================================================
当实体类的字段为实体类或者为 `List<Object>` 时,要使用嵌套校验才可以校验内部的实体类, 要在字段上再加 `@Valid` 注解
```java
@Data
public class AdInfo {
/**
* 区域统计数据
*/
@Valid
@NotNull(message = "arrival不能为空")
private Arrival arrival;
/**
* 客户群统计数据
*/
@Valid
@NotNull(message = "departure不能为空")
private List<Departure> departure;
}
```
四、分组校验
===============================================================================================
1. 新建两个接口
```java
public interface AddGroup {
}
public interface UpdateGroup {
}
```
2. 使用 `@Validated` 注解来指定使用哪个分组的校验规则,如果不指定,则只会校验没有指定分组规则,所有有 `group` 参数的规则都不会校验
```java
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Validated({UpdateGroup.class}) @RequestBody BrandEntity brand){
brandService.updateById(brand);
return R.ok();
}
```
3. 通过 `groups` 参数给字段上的注解分组,可以指定多个
```java
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
private String name;
/**
* 品牌logo地址
*/
@URL(message = "logo必须是合法的URL地址", groups = {AddGroup.class, UpdateGroup.class})
@NotEmpty(groups = {AddGroup.class})
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(vals = {0, 1}, groups = {AddGroup.class})
private Integer showStatus;
/**
* 检索首字母
*/
@Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
@NotEmpty(groups = {AddGroup.class})
private String firstLetter;
/**
* 排序
*/
@Min(value = 0, message = "排序必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
@NotNull(groups = {AddGroup.class})
private Integer sort;
}
```
五、单属性自定义校验规则
=====================================================================================================
1\. 功能:只能取枚举的值
-------------------------------------------------------------------------------------------------------
1)定义自定义校验器,实现 `ConstraintValidator` 接口
* 第一个泛型:注解
* 第二个泛型:修饰的字段类型
```java
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化方法,一般用于将自定义注解中的参数缓存
* @param constraintAnnotation 参数是自定义的注解
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
/**
*
* @param value 传过来的参数
* @param context
* @return true 校验通过
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
```
2)定义注解
* 通过 `@Constraint(validatedBy = {ListValueConstraintValidator.class})` 来指定上一步定义的校验器,可以指定多个校验器
* 注解必须有三个参数,`message`,`groups`,`payload`
```java
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
//这里指定 ValidationMessages.properties 文件中的键,一般是以注解的类路径来命名
//当校验失败时默认返回的消息
String message() default "{com.atguigu.common.valid.ListValue.message}";
//分组校验时使用
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int[] vals() default {};
}
```
3)指定默认的错误提示 `message`,在 `resource` 中新建 `ValidationMessages.properties` 文件
```java
com.atguigu.common.valid.ListValue.message="参数校验错误"
```
> * 可能出现编码错误,在 File > Settings > Editor > File Encodings,添加 ValidationMessages.properties,指定编码为UTF-8,并勾选Transparent native-to-ascii conversion,不行就重新新建该文件。参考[这篇博客](https://blog.csdn.net/qq_25844803/article/details/107660291)
4)修饰字段
```java
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(vals = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
```
六、多属性自定义联合校验规则
=======================================================================================================
1\. 功能:不同优惠券类型校验不同参数
-------------------------------------------------------------------------------------------------------------
1)实体类中自定义方法,将所有关联校验的字段都统一返回
```java
public class CouponTmplDto{
/**
* 优惠券模板类型(0 满减券,1折扣券)
*/
@NotNull
private Integer couponTmplType;
/**
* 满多少钱,规则类型为“满减券”时必填
*/
private BigDecimal fullMoney;
/**
* 减多少钱,规则类型为”满减券“时必填
*/
private BigDecimal reduceMoney;
/**
* 折扣率,规则类型为“折扣券”时必填
*/
private BigDecimal discountRate;
/**
* 注意:返回的方法要以 get 开头
*
* @return 所有所有要关联校验的参数
*/
@JsonIgnore
@CouponTmplTypeAnno(message = "优惠券类型校验失败")
public Map<String, Object> getCouponTmplTypeValidator() {
Map<String, Object> map = new HashMap();
map.put("couponTmplType", couponTmplType);
map.put("fullMoney", fullMoney);
map.put("reduceMoney", reduceMoney);
map.put("discountRate", discountRate);
return map;
}
}
```
2)自定义校验注解
```java
@Documented
@Constraint(validatedBy = {CouponTmplTypeValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RUNTIME)
public @interface CouponTmplTypeAnno {
String message() default "优惠券模板类型及金额参数校验失败";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
```
3)自定义校验器
```java
import cn.hutool.core.util.BooleanUtil;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.math.BigDecimal;
import java.util.Map;
public class CouponTmplTypeValidator implements ConstraintValidator<CouponTmplTypeAnno, Map<String, Object>> {
/**
* 自定义校验器初始化
*
* @param constraintAnnotation
*/
@Override
public void initialize(CouponTmplTypeAnno constraintAnnotation) {
//注解没有参数,不需要初始化
}
/**
* 校验方法
*
* @param map 注解在方法上时,该值是方法返回值;注解在字段上时,该值是字段值
* @param context
* @return true时校验通过,false时校验不通过
*/
@Override
public boolean isValid(Map<String, Object> map, ConstraintValidatorContext context) {
Integer couponTmplType = (Integer) map.get("couponTmplType");
BigDecimal fullMoney = (BigDecimal) map.get("fullMoney");
BigDecimal reduceMoney = (BigDecimal) map.get("reduceMoney");
BigDecimal discountRate = (BigDecimal) map.get("discountRate");
switch (CouponTmplTypeEnum.getByType(couponTmplType)) {
case FULL_REDUCT:
//满减券 满a减b a>b
return BooleanUtil.and(fullMoney != null, reduceMoney != null, fullMoney.compareTo(reduceMoney) > 0);
case DISCOUNT:
//折扣券 1位小数
return discountRate != null && discountRate.scale() <= CommonConstant.ONE;
default:
return false;
}
}
}
```
2\. 通用多属性联合校验注解
--------------------------------------------------------------------------------------------------------
如果多属性联合校验的条件不通用,每次校验都要写校验器、注解会很麻烦,这里使用匿名内部类的方式,实现每次只需要写校验逻辑即可,不用再定义校验器和注解
1)通用自定义注解:
```java
@Documented
@Constraint(validatedBy = {CustomValidator.class})
@Target({ElementType.METHOD})
@Retention(RUNTIME)
public @interface CustomValidateAnno {
@AliasFor("message")
String value() default "{com.snbc.coupon.activity.annotation.CustomValidateAnno.message}";
/**
* Message string.
*
* @return the string
*/
@AliasFor("value")
String message() default "{com.snbc.coupon.activity.annotation.CustomValidateAnno.message}";
/**
* Groups class [ ].
*
* @return the class [ ]
*/
Class<?>[] groups() default {};
/**
* Payload class [ ].
*
* @return the class [ ]
*/
Class<? extends Payload>[] payload() default {};
}
```
2)通用校验器:
```java
public class CustomValidator implements ConstraintValidator<CustomValidateAnno, BooleanSupplier> {
/**
* 自定义校验器初始化
*
* @param constraintAnnotation annotation instance for a given constraint declaration
*/
@Override
public void initialize(CustomValidateAnno constraintAnnotation) {
//不需要初始化
}
@Override
public boolean isValid(BooleanSupplier supplier, ConstraintValidatorContext context) {
//这里使用匿名内部类,实现校验器和注解通用
return supplier.getAsBoolean();
}
}
```
3)使用注解:(优化上一节中优惠券的校验规则)
```java
public class CouponTmplDto {
/**
* 优惠券模板类型(0 满减券,1折扣券)
*/
@NotNull
private Integer couponTmplType;
/**
* 满多少钱,规则类型为“满减券”时必填
*/
private BigDecimal fullMoney;
/**
* 减多少钱,规则类型为”满减券“时必填
*/
private BigDecimal reduceMoney;
/**
* 折扣率,规则类型为“折扣券”时必填
*/
private BigDecimal discountRate;
/**
* 注意:返回的方法要以 get 开头
*
* @return 校验逻辑的匿名内部类, 校验通过(true), 校验不通过(false)
*/
@CustomValidateAnno
public BooleanSupplier getCouponTmplTypeValidateResult() {
return () -> {
switch (CouponTmplTypeEnum.getByType(couponTmplType)) {
case FULL_REDUCT:
//满减券 满a减b a>b
return BooleanUtil.and(fullMoney != null, reduceMoney != null, fullMoney.compareTo(reduceMoney) > 0);
case DISCOUNT:
//折扣券 1位小数
return discountRate != null && discountRate.scale() <= CommonConstant.ONE;
default:
return false;
}
};
}
/**
* 注意:返回的方法要以 get 开头
*
* @return 校验逻辑的匿名内部类, 校验通过(true), 校验不通过(false)
*/
@CustomValidateAnno("校验失败时提示消息")
public BooleanSupplier getOtherValidateResult() {
return () -> {
System.out.println("写校验方法");
// true 校验通过
return true;
};
}
}
```
七、手动校验
===============================================================================================
手动校验 registerInfo 这个对象是否符合规范
```java
Set<ConstraintViolation<RegisterInfo>> validate = Validation.buildDefaultValidatorFactory().getValidator().validate(registerInfo);
```
声明:本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。微信:ZDVIP51888;邮箱:8122356@qq.com。
本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明,转载时需注明出处: 内容转载自: 智编生态圈👉https://www.atutil.com/article/25
本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明,转载时需注明出处: 内容转载自: 智编生态圈👉https://www.atutil.com/article/25

空余位置【270PX*270PX】
点击申请