SpringBoot数据校验与优雅处理详解

本篇要点JDK1.8、SpringBoot2.3.4release

  • 说明后端参数校验的必要性 。
  • 介绍如何使用validator进行参数校验 。
  • 介绍@Valid和@Validated的区别 。
  • 介绍如何自定义约束注解 。
  • 关于Bean Validation的前世今生
后端参数校验的必要性在开发中 , 从表现层到持久化层 , 数据校验都是一项逻辑差不多 , 但容易出错的任务 ,
前端框架往往会采取一些检查参数的手段 , 比如校验并提示信息 , 那么 , 既然前端已经存在校验手段 , 后端的校验是否还有必要 , 是否多余了呢?
并不是 , 正常情况下 , 参数确实会经过前端校验传向后端 , 但如果后端不做校验 , 一旦通过特殊手段越过前端的检测 , 系统就会出现安全漏洞 。
不使用Validator的参数处理逻辑既然是参数校验 , 很简单呀 , 用几个if/else直接搞定:
@PostMapping("/form")public String form(@RequestBody Person person) {if (person.getName() == null) {return "姓名不能为null";}if (person.getName().length() < 6 || person.getName().length() > 12) {return "姓名长度必须在6 - 12之间";}if (person.getAge() == null) {return "年龄不能为null";}if (person.getAge() < 20) {return "年龄最小需要20";}// service ..return "注册成功!";}写法干脆 , 但if/else太多 , 过于臃肿 , 更何况这只是区区一个接口的两个参数而已 , 要是需要更多参数校验 , 甚至更多方法都需要这要的校验 , 这代码量可想而知 。 于是 , 这种做法显然是不可取的 , 我们可以利用下面这种更加优雅的参数处理方式 。
Validator框架提供的便利Validating data is a common task that occurs throughout all application layers, from the presentation to the persistence layer. Often the same validation logic is implemented in each layer which is time consuming and error-prone.
如果依照下图的架构 , 对每个层级都进行类似的校验 , 未免过于冗杂 。
SpringBoot数据校验与优雅处理详解文章插图
Jakarta Bean Validation 2.0 - defines a metadata model and API for entity and method validation. The default metadata source are annotations, with the ability to override and extend the meta-data through the use of XML.
The API is not tied to a specific application tier nor programming model. It is specifically not tied to either web or persistence tier, and is available for both server-side application programming, as well as rich client Swing application developers.
Jakarta Bean Validation2.0定义了一个元数据模型 , 为实体和方法提供了数据验证的API , 默认将注解作为源 , 可以通过XML扩展源 。
SpringBoot数据校验与优雅处理详解文章插图
SpringBoot自动配置ValidationAutoConfigurationHibernate Validator是Jakarta Bean Validation的参考实现 。
在SpringBoot中 , 只要类路径上存在JSR-303的实现 , 如Hibernate Validator , 就会自动开启Bean Validation验证功能 , 这里我们只要引入spring-boot-starter-validation的依赖 , 就能完成所需 。
org.springframework.bootspring-boot-starter-validation目的其实是为了引入如下依赖:
org.glassfishjakarta.el3.0.3compileorg.hibernate.validatorhibernate-validator6.1.5.FinalcompileSpringBoot对BeanValidation的支持的自动装配定义在org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration类中 , 提供了默认的LocalValidatorFactoryBean和支持方法级别的拦截器MethodValidationPostProcessor 。
@Configuration(proxyBeanMethods = false)@ConditionalOnClass(ExecutableValidator.class)@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")@Import(PrimaryDefaultValidatorPostProcessor.class)public class ValidationAutoConfiguration { @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @ConditionalOnMissingBean(Validator.class) public static LocalValidatorFactoryBean defaultValidator() {//ValidatorFactoryLocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();factoryBean.setMessageInterpolator(interpolatorFactory.getObject());return factoryBean; }// 支持Aop , MethodValidationInterceptor方法级别的拦截器 @Bean @ConditionalOnMissingBean public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment,@Lazy Validator validator) {MethodValidationPostProcessor processor = new MethodValidationPostProcessor();boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);processor.setProxyTargetClass(proxyTargetClass);// factory.getValidator(); 通过factoryBean获取了Validator实例 , 并设置processor.setValidator(validator);return processor; }}