Spring Boot 集成fluent-validator
阅读本文之前,请先熟读官网文档
阅读总结
- 验证结果对象
// 省缺结果对象:
toSimple() ---> Result
toComplex() ---> ComplexResult
// 如果你想自己实现一个结果类型,完全可以定制,实现ResultCollector接口即可
ResultCollector<T>
- 错误消息抛出
context.addErrorMsg("Something is wrong about the car seat count!");
// 简单错误消息
context.addError(ValidationError.create("Something is wrong about the car seat count!").setErrorCode(100).setField("seatCount").setInvalidValue(t));
// 完整错误消息
- 验证器
Validator<T> // 验证器
ValidatorChain // 调用链
onEach //验证集合
failFast() // 校验一个错误打回
failOver() // 完成所有检验打回
when() // 满足条件进行校验
ValidateCallback // doValidate()方法接受一个ValidateCallback接口
......
冗余的东西就不多写了,剩下的全都融合到Spring Boot的集成里去,官方文档写的很好,推荐耐心嚼烂。
开始集成
pox.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baidu.unbiz</groupId>
<artifactId>fluent-validator-spring</artifactId>
<version>1.0.9</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建实体类Car
可以看到我们用了HibernateValidator与fluent-validator验证注解,HibernateValidator负责简单的校验,fluent-validato去校验我们更高级的业务逻辑。
package com.example.demo.entity;
import com.baidu.unbiz.fluentvalidator.annotation.FluentValidate;
import com.example.demo.fluent.validator.CarSeatCountValidator;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* @Author Z.xichao
* @Create 2018-5-30
* @Comments
*/
public class Car {
@NotEmpty(message = "厂商信息不能为空")
@Pattern(regexp = "[0-9a-zA-Z\4e00-\u9fa5]+",message = "厂商信息字符不合法")
private String manufacturer;
@NotNull(message = "车牌号不能为空")
private String licensePlate;
@FluentValidate({CarSeatCountValidator.class})
private int seatCount;
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getLicensePlate() {
return licensePlate;
}
public void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
this.seatCount = seatCount;
}
}
Validator 校验器
package com.example.demo.fluent.validator;
import com.baidu.unbiz.fluentvalidator.ValidationError;
import com.baidu.unbiz.fluentvalidator.Validator;
import com.baidu.unbiz.fluentvalidator.ValidatorContext;
import com.baidu.unbiz.fluentvalidator.ValidatorHandler;
import org.springframework.stereotype.Component;
/**
* @Author Z.xichao
* @Create 2018-5-30
* @Comments
*/
@Component
public class CarSeatCountValidator extends ValidatorHandler<Integer> implements Validator<Integer> {
@Override
public boolean validate(ValidatorContext context, Integer t) {
if (t < 2) {
context.addError(ValidationError.create("Something is wrong about the car seat count!").setErrorCode(100).setField("seatCount").setInvalidValue(t));
return false;
}
return true;
}
}
验证回调 ValidateCarCallback
package com.example.demo.validate_car_callback;
import com.baidu.unbiz.fluentvalidator.ValidateCallback;
import com.baidu.unbiz.fluentvalidator.ValidationError;
import com.baidu.unbiz.fluentvalidator.Validator;
import com.baidu.unbiz.fluentvalidator.validator.element.ValidatorElementList;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @Author Z.xichao
* @Create 2018-5-30
* @Comments
*/
@Component
public class ValidateCarCallback implements ValidateCallback {
/**
* 所有验证完成并且成功后
*
* @param validatorElementList 验证器list
*/
@Override
public void onSuccess(ValidatorElementList validatorElementList) {
System.out.println("Everything works fine!");
}
/**
* 所有验证步骤结束,发现验证存在失败后
*
* @param validatorElementList 验证器list
* @param errors 验证过程中发生的错误
*/
@Override
public void onFail(ValidatorElementList validatorElementList, List<ValidationError> errors) {
throw new RuntimeException(errors.get(0).getErrorMsg()); //可自定义异常
}
/**
* 执行验证过程中发生了异常后
*
* @param validator 验证器
* @param e 异常
* @param target 正在验证的对象
* @throws Exception
*/
@Override
public void onUncaughtException(Validator validator, Exception e, Object target) throws Exception {
throw new RuntimeException(e); //可自定义异常
}
}
配置拦截器
package com.example.demo.config;
import com.baidu.unbiz.fluentvalidator.interceptor.FluentValidateInterceptor;
import com.example.demo.validate_car_callback.ValidateCarCallback;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author Z.xichao
* @Create 2018-5-30
* @Comments
*/
@Configuration
public class FluentValidatorConfiguration {
@Autowired
private ValidateCarCallback validateCarCallback; // 注入我们的验证回调
@Bean("fluentValidateInterceptor")
public FluentValidateInterceptor fluentValidateInterceptor(){
FluentValidateInterceptor validateInterceptor = new FluentValidateInterceptor();
validateInterceptor.setCallback(validateCarCallback);
validateInterceptor.setLocale("zh_CN");
validateInterceptor.setHibernateDefaultErrorCode(10000);
return validateInterceptor;
}
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
BeanNameAutoProxyCreator proxyCreator = new BeanNameAutoProxyCreator();
proxyCreator.setBeanNames("*Controller"); // 配置拦截对象 如拦截service(*ServiceImpl)
proxyCreator.setInterceptorNames("fluentValidateInterceptor");
return proxyCreator;
}
}
好了万事俱备,只欠东风,让我们跑起来!!
package com.example.demo.controller;
import com.baidu.unbiz.fluentvalidator.annotation.FluentValid;
import com.example.demo.entity.Car;
import com.example.demo.service.CarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author Z.xichao
* @Create 2018-5-30
* @Comments
*/
@RestController
public class TestController {
@Autowired
private CarService carService;
@RequestMapping("/")
public void root(@FluentValid Car car) {
carService.addCart(car);
}
}
访问http://localhost:8080/?manufacturer=aa&licensePlate=b
2018-05-30 17:47:24.128 INFO 5108 --- [ restartedMain] .ConditionEvaluationDeltaLoggingListener : Condition evaluation unchanged
2018-05-30 17:47:44.171 INFO 5108 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-05-30 17:47:44.171 INFO 5108 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2018-05-30 17:47:44.174 INFO 5108 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 3 ms
2018-05-30 17:47:44.182 INFO 5108 --- [nio-8080-exec-1] c.b.u.f.AnnotationValidatorCache : Cached validator CarSeatCountValidator
2018-05-30 17:47:44.187 ERROR 5108 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: [ValidationError{errorCode=100, errorMsg='Something is wrong about the car seat count!', field='seatCount', invalidValue=0}]] with root cause
java.lang.RuntimeException: [ValidationError{errorCode=100, errorMsg='Something is wrong about the car seat count!', field='seatCount', invalidValue=0}]
at com.example.demo.validate_car_callback.ValidateCarCallback.onFail(ValidateCarCallback.java:36) ~[classes/:na]
at com.baidu.unbiz.fluentvalidator.FluentValidator.doValidate(FluentValidator.java:518) ~[fluent-validator-1.0.9.jar:na]
at com.baidu.unbiz.fluentvalidator.interceptor.FluentValidateInterceptor.invoke(FluentValidateInterceptor.java:189) ~[fluent-validator-spring-1.0.9.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at com.example.demo.controller.TestController$$EnhancerBySpringCGLIB$$feb78357.root(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) ~[spring-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
结语
本篇博客能算个学习笔记,然后拿大家一起分享。重在抛砖引玉,可能以后会继续完善,先留几个坑。
比如context上下文共享、闭包、以及验证器的配置、when()、failFast()、failOver()等都要考虑怎么活用。总之先到这吧。各位道友,我们昆仑山见!