API接口参数验证的必备神器,让你的代码更高效!

开发 前端
类上必须有@Validated注解;因为上面的BeanPostProcessor中定义的Advisor(DefaultPointcutAdvisor)使用的切入的Pointcut在类级别上过滤条件是必须有@Validated注解,而方法则是拦截所有的方法。

环境:Springboot2.6.12

1. 简介

Spring Validation是一种轻量级的数据验证框架,主要用于对Java对象进行校验。它为数据验证提供了统一的接口和基本的校验功能,解决了数据校验这一常见问题,让开发人员能够方便地对数据进行验证,从而保证数据的有效性和安全性。

Spring Validation提供了一套注解,用于对Java对象进行校验,支持嵌套校验和分组校验,支持国际化和自定义注解和校验器,可以满足各种复杂的校验需求。它的主要特点包括:

  1. 轻量级:Spring Validation只做验证相关的事情,不包含复杂的业务逻辑。
  2. 简单易用:基于注解,简洁明了,易于维护。
  3. 校验规则灵活:支持自定义校验规则,可扩展性强。
  4. 支持国际化:根据不同的语言环境,使用不同的校验提示消息。
  5. 集成Hibernate Validator:Spring Validation默认使用Hibernate Validator作为其具体的实现,可以轻松地与其他数据验证框架一起工作。

Bean Validation 为Java应用程序提供了一种通过约束声明和元数据进行验证的通用方法。要使用它,只需要对POJO属性进行注释,然后由运行时强制执行这些约束。有内置的约束,你也可以定义自己的自定义约束。如下所示:

public class Person {


  @NotNull
  @Size(max=64)
  private String name;
  @Min(0)
  private int age;


}

Bean验证验证器然后根据声明的约束验证此类的实例。有关API的一般信息,请参见Bean验证。有关特定约束,请参阅Hibernate验证程序文档。

配置Bean验证提供程序

Spring提供了对Bean验证API的全面支持,包括将Bean验证提供者作为Spring Bean。这使你可以在应用程序中需要验证的任何位置注入javax.validation.ValidatorFactory或javax.validation.Validator。

你可以使用LocalValidatorFactoryBean将默认验证器配置为Spring Bean,如下例所示:

import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;


@Configuration
public class AppConfig {


  @Bean
  public LocalValidatorFactoryBean validator() {
    return new LocalValidatorFactoryBean();
  }


}

上面的示例中的基本配置使用默认引导机制触发bean验证进行初始化。Bean验证提供程序(如Hibernate验证程序)应该出现在类路径中,并被自动检测到。

2. 注入 Validator

@Service
public class PersonService {
  // inject javaee validator object
  @Resource
  private Validator validator ;
  // inject spring validator object
  @Resource
  private org.springframework.validation.Validator valid ;
}

简单实例

接着上面的配置,我们只需要做验证动作即可。

@Service
public class PersonService {


  @Resource
  private Validator validator ;
  @Resource
  private org.springframework.validation.Validator valid ;




  public void validator(Person person) {
    Set<ConstraintViolation<Person>> res = validator.validate(person) ;
    res.forEach(cv -> {
      System.out.println(cv.getMessage()) ;
    });
    System.out.println("----------------------") ;
    BindingResult errors = new MapBindingResult(new HashMap<String, Object>(), "person") ;
    valid.validate(person, errors) ;
    if (errors.hasErrors()) {
      errors.getAllErrors().forEach(oe -> {
        System.out.println(oe.getDefaultMessage()) ;
      });
    }
  }


}

测试

@SpringBootTest
class SpringBootValidationApplicationTests {


  @Resource
  private PersonService ps ;


  @Test
  public void testValidator() {
    Person person = new Person() ;
    person.setAge(-1);
    ps.validator(person) ;
  }


}
最小不能小于0
不能为null
----------------------
最小不能小于0
不能为null

3. 自定义注解验证

每个Bean验证约束由两部分组成:

  • 声明约束及其可配置属性的@Constraint注释。
  • 实现约束行为的javax.validation.ConstraintValidator接口的实现。

要将声明与实现关联,每个@Constraint注释都会引用相应的ConstraintValidator实现类。在运行时,当域模型中遇到约束注释时,ConstraintValidatorFactory将实例化引用的实现。下面的示例实现一个前缀匹配的验证逻辑:

自定义注解

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PrefixConstraintValidator.class)
public @interface PrefixConstraint {


  String value() default "" ;
  // 这里的{validator.prefix.error}就是资源文件中定义的错误信息
  String message() default "{validator.prefix.error}";


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


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


}

以上的注解属性都是必须的。

注意message属性是我们将发生错误后错误信息定义在配置文件中,而该文件的basename必须是ValidationMessages,如果你需要国际化支持,那么就这样命名:ValidationMessages_zh_CN.properties。

自定义验证器

public class PrefixConstraintValidator implements ConstraintValidator<PrefixConstraint, CharSequence> {


  @Resource
  private DataService ds ;


  private String prefix ;


  @Override
  public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
    ds.ak() ;
    if ( value == null ) {
      return false ;
    }
    return ((String) value).startsWith(prefix) ;
  }


  @Override
  public void initialize(PrefixConstraint pc) {
    prefix = pc.value() ;
  }


}

验证器必须实现ConstraintValidator接口,该接口是泛型接口,第一个参数是该验证器要用于在那个注解,第二个参数是该注解应用在什么数据类型上。注意:在自定义验证器中我们是可以随意地注入其它Bean对象,是不是很强大?

基于方法级的验证

你可以通过MethodValidationPostProcessor Bean定义将Bean validation 1.1(以及Hibernate Validator 4.3的自定义扩展)支持的方法验证功能集成到Spring上下文中:

@Bean
public MethodValidationPostProcessor validationPostProcessor() {
  return new MethodValidationPostProcessor();
}
@Service
@Validated
public class PersonService {


  @NotNull(message = "返回值不能为空")
  public Person findPerson(@NotEmpty(message = "ID 不能为空") String id) {
    return null ;
  }


}

注意:类上必须有@Validated注解;因为上面的BeanPostProcessor中定义的Advisor(DefaultPointcutAdvisor)使用的切入的Pointcut在类级别上过滤条件是必须有@Validated注解,而方法则是拦截所有的方法。

测试

图片

这里是抛出的异常javax.validation.ConstraintViolationException,所有我们需要一个全局的异常拦截器来对异常做处理。

其他配置选项

默认的LocalValidatoryFactoryBean配置对于大多数情况都足够了。对于各种Bean验证构造,有许多配置选项,从消息插值到遍历解析。有关这些选项的更多信息,请参阅LocalValidatorFactoryBean Javadoc。

https://docs.spring.io/spring-framework/docs/5.3.11/javadoc-api/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.html

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2010-12-23 15:55:00

上网行为管理

2024-02-26 16:40:58

2023-07-26 07:41:53

Python线程状态

2019-04-29 08:31:25

PythonPandas数据

2023-07-28 07:31:56

FFmpeg开源

2011-08-29 09:33:48

2015-07-03 10:46:26

PHP程序员工作高效

2019-11-25 10:20:54

CSS代码javascript

2018-05-08 14:58:07

戴尔

2018-09-16 22:46:18

Mock单元测试集成测试

2019-01-29 05:34:47

GitHub插件代码

2019-09-29 16:17:25

Java代码性能编程语言

2023-11-24 11:20:04

functoolsPython

2019-04-19 08:47:00

前端监控数据

2016-06-30 16:54:49

UCloud爱数云计算

2024-04-26 11:54:10

Pygments代码Pytho

2013-07-23 10:50:24

C程序

2023-11-16 08:55:14

CSS前端

2023-06-27 08:41:35

DapperSQL语句

2018-06-20 11:00:06

云应用开发PaaS
点赞
收藏

51CTO技术栈公众号