后端验证 - JSR-303

后端验证 - JSR-303

编写信息

笔者:LiiiYiAn

编写日期:2023.05.20

参考文献

[9] 稀土掘金 - 一文学会JSR-303 参数校验,真香 参考日期:2023.05.20

[8] CSDN - 使用Hibernate-Validator实现参数校验 参考日期:2023.05.20

[7] 百度百科 - JCP 参考日期:2023.05.20

[6] 简书 - Hibernate Validator简介 参考日期:2023.05.20

[5] CSDN - 3. 什么是JSR参考实现? - JavaEE基础系列 参考日期:2023.05.20

[4] 博客园 - JSR-303简单使用 参考日期:2023.05.20

[3] 书籍 - Spring 实战第 5 版-【美】克雷格·沃斯(Craig Walls) 著 张卫滨 译 参考日期:2023.05.20

[2] 脚本之家 - SpringBoot后端进行数据校验JSR303的使用详解 参考日期:2023.05.20

[1] CSDN - JSR303的基本使用 参考日期:2023.05.20

序论

为什么需要后端验证?

前后端进行数据交互时,在前端把数据传送到后端前,一般会先验证一次,验证成功之后,才把数据发送到后端。

但是我们在后端还得在对数据进行一次校验。因为请求数据发送的链接很容易获取,可以不经过前端界面,使用 Postman 等工具直接向后台发送数据,这就可能会造成传送数据不合法的情况

早期的参数校验形式

在早期的时候,Java 的参数校验停留在获取参数之后在代码层面做校验,类似如下操作:

@PostMapping("/test")
public String test(@RequestBody TestRequest request) {
  if (StringUtils.isEmpty(request.getName())) {
      return "姓名不能为空";
  }
  if (request.getName().length() > 6) {
      return "姓名长度不能超过6";
  }
  if (StringUtils.isEmpty(request.getPhone())) {
      return "电话不能为空";
  }
  if (!isPhone(request.getPhone())) {
      return "电话号码格式不正确";
  }

  return "SUCCESS";
}

/**
 * 校验电话格式
 */
private boolean isPhone(String phone) {
  return true;
}

如上,每一种校验规则都需要在代码中实现,那么光是参数校验就会耗费开发人员大量的精力,进而导致开发效率大幅降低

JSR-303 相关概念

JSR-303 中的 JSR 是何

进入正文,首先了解下 JSR-303 中的 JSR 是什么意思

JSR 是 Java Specification Requests 的缩写,意思是 Java 规范提案。是指向 JCP 提出新增一个标准化技术规范的正式请求。

JCP:Java Community Process,一个开放的国际组织,主要由 Java 开发者以及被授权者组成,职能是发展和更新

任何人都可以向 JCP 提交 JSR,以向 Java 平台增添新的 API 和服务。JSR 已成为 Java 界的一个重要标准。

JSR 就是一个提交到 JCP 的抽象请求,包含对 Java 技术平台的补充。因为是抽象的,所以不能直接被调用

JCP 官网中关于 JSR:https://jcp.org/en/jsr/overview

主角 JSR-303 登场

然后,再来介绍我们的主角 JSR-303

为什么叫 JSR-303 呢?

JSR 是 Java 规范提案。JSR-303 也就是第 303 号提案

JSR-303 是 Java EE 6 中的一项子规范,叫做 Bean Validation,即 Bean 验证,顾名思义,它提供了对 Java EE 和 Java SE 中的 Java Bean 中字段的值进行验证的方式。

Validation /ˌvæləˈdeɪʃən/ n.验证,确认

Spring MVC 中也支持 JSR-303,我们可以在控制器中对表单提交的数据方便地验证。

该规范主要使用注解的方式来实现对 Java Bean 的验证功能

JSR-303 与 JS 区别:

  • JSR-303 属于后端验证
  • JS 属于前端的脚本验证(只作用于客户端的浏览器中,可以被屏蔽)

通过使用 JSR-303,我们能够更容易地声明验证规则,而不必在应用代码中显式编写声明逻辑

JCP 官网中关于 JSR-303:https://jcp.org/en/jsr/detail?id=303

JSR-303 的一个参考实现Hibernate Validator,Hibernate Validator 提供了 JSR-303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint

Hibernate Validator 官方文档(全英文)

关于这个参考实现,想必很多人都是一脸懵,下文会详解

Hibernate Validator 与 Hibernate ORM 框架没有任何关系,Hibernate 项目不仅有 ORM 一个框架

拓展 - 关于参考实现

参考实现:Reference Implementation

JSR 是一个提交到 JCP 的抽象请求,包含对 Java 技术平台的补充。

因为是抽象的,所以不能直接被调用。JSR 需要有某种形式的实现,或者说开发人员能直接使用的某种具现。这就是参考实现的概念。

JCP 规定 每个 JSR 都必须有参考实现,和 JSR 文档打包在一起。 其具体实现公众可以自由使用,一般和应用服务器打包在一起发布。

参考实现不止一个,这也是上文为什么说 Hibernate Validator 是 JSR-303 的一个参考实现

每个 JSR 也都包含 TCK,这是一套测试工具, 用于检测 JSR 实现是否符合规范的要求。

TCK:Technology Compatibility Kit,技术兼容性工具包

TCK 用来测试 JSR 实现是否符合规范。这也从侧面说明,为什么 Java EE 是一套标准化规范

这种严格的审核过程确保了 JSR 文档中 API 的质量。下面是一些流行的消息格式的 JSR:

这些都是新的 JSR 和各自的参考实现。大多数参考实现都和应用服务器捆绑在一起

在项目中使用 JSR-303 的前提

在非 Spring Boot 项目中使用 JSR-303 前提:

在非 Spring Boot 项目中使用 JSR-303,使用的是其参考实现 Hibernate Validator

  • 普通项目,引入相关 jar 包,版本自定:

    validation-api-1.0.0.GA.jar  // JDK的接口
    hibernate-validator-4.2.0.Final.jar  // 对上述接口的实现
    
  • Maven 项目,在 pom.xml 中引入对应依赖,版本自定:

    	  <!-- JSR-303 -->
    	  <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>6.0.7.Final</version>
          </dependency>
    

在 Spring Boot 项目中使用 JSR-303,引入依赖:

<!-- JSR-303 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

需要注意的一点是,关于在 Spring Boot 项目中使用 JSR-303,《Spring 实战第 5 版》这样说:

JSR-303 和 JSR-303 的 Hibernate 实现会作为 Spring Boot web starter传递性依赖会自动添加到项目中

Spring Boot web starter:Spring Boot web 启动程序
传递性依赖:临时依赖项

这里需要纠正的是:老版本的确是默认引入,但在新版本的 Spring Boot 中,需要单独引入

应该是 Spring 2.3.x 开始取消了默认引入 JSR-303。可以先试试在 IDEA 中的实体类属性上打一个注解 @Size,如果弹出引入包提示就说明默认引入了,否则就需要单独引用

JSR-303 内置验证注解

注解签名注解功能
@Null被注解的元素必须为 null
@NotNull被注解的元素必须不为 null
@NotBlank字符串必须不为 null,且去掉前后空格长度必须大于 0
@AssertTrue被注解的元素必须为 true
@AssertFalse被注解的元素必须为 false
@Min(value)value 为 int,被注解的元素必须是一个数字,其值必须大于等于 value
@Max(value)value 为 int,被注解的元素必须是一个数字,其值必须小于等于 value
@DecimalMin(value)value 为 int,被注解的元素必须是一个数字,其值必须大于等于 value
@DecimalMax(value)value 为 int,被注解的元素必须是一个数字,其值必须小于等于 value
@Size(max, min)对象 (Array、Collection、Map、String) 长度必须在给定范围
@Digits (integer, fraction)被注解的元素必须是一个数字,integer 指定整数精度,fraction 指定小数精度 。例如 @Digits(integer=3, fraction=0) 表示值包含 3 位数字
@Past被注解的元素必须是一个过去的日期
@Future被注解的元素必须是一个将来的日期
@Pattern(regex, flag)被注解的元素必须符合指定的正则表达式

使用 @NotNull 时注意 import,因为有两种 @NotNull

在这里插入图片描述

注意要选择 JSR-303 的,也就是 javax.validation.constraints.NotNull

注解一般是在 javax.validation.constraints 这个包下,也还有一些是 hibernate 提供的

同时,根据 JSR-303 规范,每个注解中都默认有 3 个属性

  • message 属性,用于定义验证错误时的提示信息,如:

    @NotBlank(message = "名称不能为空")
    private String name;
    

    当 name 属性验证错误,即为空时,就会提示该注解对应 message 属性设置的提示信息

  • groups 属性,用于分组验证

    groups 属性在后文 @Vaild 和 @Vaildated 中会详解

  • payload 属性,定义负载信息

Hibernate Validator 附加验证注解

Hibernate Validator 是 JSR-303 的一个参考实现,除了支持所有标准的校验注解外,另外 Hibernate Validator 还有 JSR-380 的实现

以下注解都是 Hibernate Validator 自定义的,假如使用其他的 JSR-303 参考实现,可能没有这些注解

注解名称注解功能
@Email被注解的元素必须是电子邮箱地址
@Length(min, max)被注解的字符串的大小必须在指定的范围内
@NotEmpty被注解的字符串的必须非空
@Range(min, max)被注解的元素必须在合适的范围内
@URL(protocol, host, port)检查是否是一个有效的 URL,如果提供了 protocol,host 等,则该 URL 还需满足提供的条件
@CreditCardNumber声明该属性值必须是通过 Luhn 算法检查有效信用卡号,这可以防止用户出错的数据和故意错误的数据,但不能保证信用卡号码实际上被分配到一个帐户,或该帐户可以用于交易

Luhn 算法检查:https://en.wikipedia.org/wiki/Luhn_algorithm

使用 JSR-303 进行参数验证的基本流程

还记得 序论 中的早期的参数校验形式代码吗?

@PostMapping("/test")
public String test(@RequestBody TestRequest request) {
  if (StringUtils.isEmpty(request.getName())) {
      return "姓名不能为空";
  }
  if (request.getName().length() > 6) {
      return "姓名长度不能超过6";
  }
  if (StringUtils.isEmpty(request.getPhone())) {
      return "电话不能为空";
  }
  if (!isPhone(request.getPhone())) {
      return "电话号码格式不正确";
  }

  return "SUCCESS";
}

/**
 * 校验电话格式
 */
private boolean isPhone(String phone) {
  return true;
}

在这个验证业务上,使用 JSR-303 将其改良

使用 JSR-303 很简单,我们只需要在想要验证的属性上加注解即可

首先在实体类中对属性添加对应验证规则的注解:

@Data
public class TestRequest {
  @NotEmpty(message = "姓名不能为空")
  @Length(min = 1, max = 6, message = "姓名长度必须在1-6之间")
  private String name;

  @NotBlank(message = "电话号码不能为空")
  @Pattern(regexp = "^134[0-8]\\d{7}$|^13[^4]\\d{8}$|^14[5-9]\\d{8}$|^15[^4]\\d{8}$|^16[6]\\d{8}$|^17[0-8]\\d{8}$|^18[\\d]{9}$|^19[8,9]\\d{8}$", message = "电话号码格式不正确")
  private String phone;

  @Email(message = "邮件格式不正确")
  private String email;

  @NotNull(message = "年龄不能为空")
  @Max(value = 18, message = "年龄不能超过18")
  private Integer age;
}

但这不够,我们还需要在控制器类中,通过 @Valid 开启校验功能

在控制器类中,用 @Valid 对前端传送进来的数据进行验证

@PostMapping("/test")
public String test(@RequestBody @Valid TestRequest request) {
    return "SUCCESS";
}

@Valid 注解来自包 javax.validation.Valid,它会对被注解的属性进行验证,验证时机是在绑定完前端传送数据之后,调用控制器方法之前

@Validated 与 @Valid 类似,在基本使用中没有区别,更多下文会讲解

前端传送数据:一般是通过表单传送

在这个示例中,被注解的属性是 TestRequest 对象属性 request,控制器方法是 test()

效果:

在这里插入图片描述

总结,使用 JSR-303 进行验证一般需要两步:

  1. 给 Bean 添加校验注解
  2. 在 Controller 中通过 @Valid 开启校验功能

@Valid 和 @Validated

基本理解

首先看一下他们所属的包:

  • @Validated :org.springframework.validation.annotation.Validated
  • @Valid:javax.validation.Valid

可以看到 @Validated 属于 spring,而 @Valid 属于 javax。

但是在实际的基本使用中,这二者是没有区别的,都会对被注解的属性进行验证,验证时机是在绑定完前端传送数据之后,调用控制器方法之前

注意是基本使用,也就是说,还是有区别的

比如说分组验证只能用 @Validated,嵌套验证只能用 @Vaild,这些下文都会详讲

比如,以上文的示例为例,将 @Vaild 替换为 @Vaildated:

@PostMapping("/test")
public String test(@RequestBody @Validated TestRequest request) {
    return "SUCCESS";
}

将注解替换后,也能得到相同的结果:

在这里插入图片描述

两个注解源码

  • @Valid
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE     })
@Retention(RUNTIME)
@Documented
public @interface Valid {
}
  • @Validated
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
  Class<?>[] value() default {};
}

通过源码可以看到 @Validated 可以在类上面使用,并且多了一个 value 属性。

这个 value 属性是干嘛的呢?

@Validated 提供了一个分组功能,在校验参数时,可以根据不同的分组采用不同的校验机制。value 属性就是用于指定这个分组

如果 @Validated 没有添加分组属性 value,则默认验证没有分组功能

@Validated 分组验证

在不同业务场景下,校验规则是不一样的,比如 user 对象中 id 这个属性,在新增时,这个属性是不用填的,要为 null,但是在修改时,id 属性是不能为 null 的

可以用注解中的 groups 属性来指明:在指定场景下使用该注解

验证分组标识类,下面四个接口代表增删改查操作:

public class ValidatedGroup {
  public interface Add extends Default {}
  public interface Delete extends Default {}
  public interface Update extends Default {}
  public interface Query extends Default {}
}

测试请求类:

@Data
public class TestSaveRequest {

  @NotNull(groups = {ValidatedGroup.Update.class, ValidatedGroup.Delete.class}, message = "更新或者删除时id不能为空")
  private Long id;

  @NotBlank(groups = {ValidatedGroup.Update.class}, message = "更新时姓名不能为空")
  private String name;

  @NotNull(groups = {ValidatedGroup.Add.class}, message = "新增时余额不能为空")
  @Digits(integer = 10, fraction = 2)
  private BigDecimal balance;

  @NotBlank(groups = {ValidatedGroup.Add.class}, message = "新增时电话不能为空")
  @Pattern(regexp = "^134[0-8]\\d{7}$|^13[^4]\\d{8}$|^14[5-9]\\d{8}$|^15[^4]\\d{8}$|^16[6]\\d{8}$|^17[0-8]\\d{8}$|^18[\\d]{9}$|^19[8,9]\\d{8}$", message = "电话号码格式不正确")
  private String phone;

  @NotBlank(groups = {ValidatedGroup.Add.class}, message = "新增时邮件不能为空")
  @Email
  private String email;
}

根据编写的测试请求类中属性上面注解的属性 groups,可预期:

  • 验证分组为 Update 时,会验证 id、name 属性
  • 验证分组为 Add 时,会验证 balance、phone、email 三个属性

测试如下:

@PostMapping("/testAdd")
public String testAdd(@RequestBody @Validated(value = ValidatedGroup.Add.class) TestSaveRequest request) {
    return "SUCCESS";
}

@PostMapping("/testUpdate")
public String testUpdate(@RequestBody @Validated(value = ValidatedGroup.Update.class) TestSaveRequest request) {
    return "SUCCESS";
}

“/testAdd” 请求结果:

在这里插入图片描述

“/testUpdate” 请求结果:

在这里插入图片描述

结果与预期完全符合。@Validated 在分组校验时,可以节省很多额外的开发,特别是当新增和更新时

一个需要传递主键一个不需要传递主键的情形,以前需要一个 AddRequest、一个 UpdateRequest,现在只需要一个注解标明就够了。

@Valid 嵌套验证

先来看看什么是嵌套验证

上代码:

实体类:

@Data
public class TestNestRequest {

  @NotNull(message = "id不能为空")
  private Long id;

  @NotNull(message = "嵌套对象请求数据不能为空")
  private ItemRequest itemRequest;
}

实体类中嵌套的实体类:

@Data
public class ItemRequest {
  @NotEmpty(message = "name不能为空")
  private String name;
}

控制器类:

@PostMapping("/testNest")
public String testNest(@RequestBody @Valid TestNestRequest request) {
  return "SUCCESS";
}

测试结果:

在这里插入图片描述

发现有什么错误吗?

只验证了 id,没有验证嵌套对象 itemRequest 中的属性

要进行嵌套验证,需要明白一点:嵌套验证必须用 @Valid

首先,改造一下 TestNestRequest 类,在 itemRequest 属性上加上 @Valid,才能验证嵌套对象中的属性:

@Data
public class TestNestRequest {

  @NotNull(message = "id不能为空")
  private Long id;

  @Valid
  @NotNull(message = "嵌套对象请求数据不能为空")
  private ItemRequest itemRequest;
}

测试结果:

在这里插入图片描述

可以看到嵌套对象验证成功

验证失败提示信息

默认提示信息

当验证失败的时候,会抛出两种异常:

使用 @Valid 校验时抛出:

  • org.springframework.validation.BindException

使用 @Validated 校验时抛出:

  • org.springframework.web.bind.MethodArgumentNotValidException

假设定义了这样一个 Bean:

@Data
public class User {
 @Length(min = 4,max = 8)
 private String password;
}

控制器类省略

在 Bean 中,我们并没有定义验证失败提示信息

当验证失败后,会出现如下错误。并且会给出默认的提示信息:

在这里插入图片描述

这个错误信息是怎么来的呢

顺便打开一个注解的源码,比如 @NotNULL :

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface NotBlank {
 
    String message() default "{javax.validation.constraints.NotNull.message}";

    //...
}

看到有一个 message 属性。显然就是指定错误的提示内容。

而这些错误提示是在一个叫 validationMessages.properties 的文件中,用 IDEA 的搜索工具可以找到,双击 shift 打开搜索

发现有这么多 validationMessages.properties 的文件,而且支持国际化:

在这里插入图片描述

打开validationMessages_zh.properties,可以看到里面定义了很多提示。而错误提示就是从这文件中获取的:

在这里插入图片描述

自定义提示信息

如果我们不想用默认的校验提示信息的话,可以自己指定,通过注解属性 message 即可:

@NotBlank(message = "用户名不能为空")
private String username;

在这里插入图片描述

提示信息的获取与响应

当验证失败时,会默认返回一个错误界面,或者返回错误提示的 JSON 数据

但默认提供的显然不是我们想要的,如果可以拿到错误信息,那我们就能自定义相应数据了。

拿到错误信息的方式也很简单,只要在方法中加上 BindingResult result 这个参数,错误信息就会被封装在这个参数中

@PostMapping("/user2")
@ResponseBody
public R user2(@Valid User user, BindingResult result) throws Exception {
  System.out.println(user);
    
  if(result.hasErrors()) {  // 判断是否有错误 
  	Map<String,String> map = new HashMap<>();
      
  	// 获取校验的错误结果
  	result.getFieldErrors().forEach((item)->{
  		// 获取到错误提示
  		String message = item.getDefaultMessage();
  		// 获取错误的属性的名字
  		String field = item.getField();
        
  		map.put(field,message);
  	});
      
  	return R.error(400,"提交的数据不合法").put("data",map);
  }
    
  // 若没有错误,则进行接下去的业务操作。
  return null;
}

不过不推荐上面这种方式,当校验的地方多了,每个方法里面都加上这么个异常处理,会让代码很臃肿。

臃肿就代表着不优雅,对于一个程序员来说,这是难以忍受的

Spring MVC 中有个全局的异常处理,我们可以自定义一个异常处理,在这里面统一处理异常

统一处理 BinException。这样就可以不用在 Controller 中去处理错误信息了:

package cn.jxj4869.demo.execption;
 
import cn.jxj4869.demo.entity.R;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
 
 
import java.util.HashMap;
import java.util.Map;
 
@RestControllerAdvice(basePackages = "cn.jxj4869.demo.controller")
public class MyExceptionControllerAdvice {
 
 @ExceptionHandler(value = BindException.class)
 public R handleVaildException(BindException e) {
 
   Map<String,String> map = new HashMap<>();
     
   // 获取校验的错误结果
   e.getFieldErrors().forEach((item)->{
    // 获取到错误提示
    String message = item.getDefaultMessage();
    // 获取错误的属性的名字
    String field = item.getField();
       
    map.put(field,message);
   });
     
   return R.error(400, "提交的数据不合法").put("data", map); 
 }
}

自定义验证注解

当提供的注解不能满足我们需求的时候,可以自定义注解。

例如我们现在给 user 新加一个属性 status,并要求这个属性的值只能是 0 或者 1。而在 JSR-303 中并没有这样的注解,因此,我们需要自定义注解

根据 JSR-303 规范,验证注解得有三个属性:

  • message:定义验证错误信息
  • groups:指定校验分组
  • payload:定义负载信息

新建一个 @StatusValue 注解,使用 @Constraint 指定该注解的验证器

package cn.jxj4869.demo.valid;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = { StatusValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@interface StatusValue {
 String message() default "{cn.jxj4869.valid.StatusValue.message}";

 Class<?>[] groups() default { };

 Class<? extends Payload>[] payload() default { };

 int[] value() default { };
}

@Constraint(validatedBy = { StatusValueConstraintValidator.class }) 指定该注解的验证器

接下来,我们需要自定义刚刚指定注解的验证器:

需要实现 ConstraintValidator 这个接口,第一个泛型是表示要校验哪个注解,第二个泛型是要校验的数据的类型

  • initialize() 是初始化方法
  • isValid() 是校验方法,判断是否校验成功

StatusValueConstraintValidator.java

package cn.jxj4869.demo.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class StatusValueConstraintValidator implements ConstraintValidator<StatusValue,Integer> {

 private Set<Integer> set = new HashSet<>();
 //初始化方法
 @Override
 public void initialize(StatusValue constraintAnnotation) {

  int[] value = constraintAnnotation.value();
  for (int val : value) {
   set.add(val);
  }

 }
 /**
  * 判断是否校验成功
  * @param value
  * @param context
  * @return
  */
 @Override
 public boolean isValid(Integer value, ConstraintValidatorContext context) {

  return set.contains(value);
 }
}

最后在resources目录下添加一个 ValidationMessages.properties 文件,用来指定错误信息

文件内容如下:

cn.jxj4869.valid.StatusValue.message=必须提交指定的值

定义一个 UserBean 作为测试:

@Data
public class User {
    @Null(groups = {AddGroup.class})
    @NotNull(groups = {UpdateGroup.class})
    private Integer id;


    @NotBlank(message="用户名不能为空", groups={AddGroup.class,UpdateGroup.class})
    private String username;
    @NotEmpty
    private String password;
    private String email;

    @StatusValue(value = {0,1},groups = {AddGroup.class,UpdateGroup.class})
    private Integer status;

传送不合法数据:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mDOcrBa4-1684596842171)(D:\Typora本地图像存放\image-20230520210657205.png)]

传送合法数据:

param value

  • @param context
  • @return
    */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

return set.contains(value);
}
}


最后在`resources`目录下添加一个 `ValidationMessages.properties` 文件,用来指定错误信息

文件内容如下:

cn.jxj4869.valid.StatusValue.message=必须提交指定的值


定义一个 UserBean 作为测试:

```java
@Data
public class User {
    @Null(groups = {AddGroup.class})
    @NotNull(groups = {UpdateGroup.class})
    private Integer id;


    @NotBlank(message="用户名不能为空", groups={AddGroup.class,UpdateGroup.class})
    private String username;
    @NotEmpty
    private String password;
    private String email;

    @StatusValue(value = {0,1},groups = {AddGroup.class,UpdateGroup.class})
    private Integer status;

传送不合法数据:

在这里插入图片描述

传送合法数据:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值