一:引入JSR303,使其支持自带的基本校验功能
- 在pom.xml文件中添加相关依赖
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
hibernate-validator是JSR 303规范实现框架。
2. spring配置文件
启用注解,并且打开Spring对JSR 303的支持,另外扫描指定包下的Controller进行实例化
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<mvc:annotation-driven />
<context:component-scan base-package="net.csdn.blog.chaijunkun.controller" />
- controller层使用
在Controller参数前加入@Validated注解。如果指定特别的编组,需要将编组class作为参数附加给该注解。最后一个参数定义为BindingResult类型。这样,在进入该Controller方法后使用result.hassErrors()方法来判断参数是否通过了约束验证。若没通过,可以通过BindingResult对象来获取详细的错误信息。
看到这,是不是觉得很麻烦,是的。下面使用切面编程来自动帮我们做参数验证检查。
在第二步配置后面追加切面配置,如下:
<!-- JSR 303验证切面 -->
<bean id="jsrValidationAdvice" class="com.hletong.framework.core.support.validation.JSRValidationAdvice" />
<aop:config>
<aop:pointcut id="jsrValidationPC" expression="execution(public * com.hletong.*.web.controller.*.*.*(..))" />
<aop:aspect id="jsrValidationAspect" ref="jsrValidationAdvice">
<aop:around method="aroundMethod" pointcut-ref="jsrValidationPC" />
</aop:aspect>
</aop:config>
下面是我们自定义切面处理逻辑:
/**
* 切点处理
* @param joinPoint
* @return
* @throws Throwable
*/
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
BindingResult result = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length != 0){
for (Object object : args) {
if (object instanceof BindingResult){
result = (BindingResult)object;
break;
}
}
}
if (result != null && result.hasErrors()){
FieldError fieldError = result.getFieldError();
String targetName = joinPoint.getTarget().getClass().getSimpleName();
String method = joinPoint.getSignature().getName();
logger.info("验证失败.控制器:{}, 方法:{}, 参数:{}, 属性:{}, 错误:{}, 消息:{}", targetName, method, fieldError.getObjectName(), fieldError.getField(), fieldError.getCode(), fieldError.getDefaultMessage());
String retMsg = "请求参数" + fieldError .getField() + fieldError.getDefaultMessage();
return ResultBuilder.buildFailResult(null, retMsg);
}
return joinPoint.proceed();
}
至此,在进入Controller方法前会先调用该切面的aroundMethod,进入切面方法后,遍历Controller的所有参数类型,看下有没有BindingResult类型的参数。如果有,就调用它,判断是否有错误。如果有错误,通过日志将详细信息输出。并且返回错误信息。如果没有错误,执行切点的proceed()方法,按预定Controller逻辑进行处理。
二:扩展自定义注解和校验器,支持消息国际化配置
增加如下配置:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="defaultEncoding" value="UTF-8" />
<!-- you can config a single basename -->
<!--<property name="basename" value="" />-->
<!-- multi basename -->
<property name="basenames">
<list>
<value>/WEB-INF/resources/errors</value>
</list>
</property>
</bean>
资源文件放在web-inf/resources目录下,文件名为errors_zh_CN.properties。
在前面提到的第二步,定义validator时,注入对应的属性值,如下:
<!-- JSR 303 Validator -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
<property name="validationMessageSource" ref="messageSource"/>
</bean>
至此,我们可以在自定义校验器的基础上,定义自己的错误返回信息。
开发过程中遇到了javax.validation.ParameterNameProvider这个问题,原因是jar包版本不匹配。
由于hibernate-validator-5.x.x已经不兼容validation-api-1.0.x,这是因为在hibernate-validator-5.x.xhibernate-validator-5.x.x已经把旧的校验框架JSR-303,改变为JSR-349了。
解决方案:
1> 把hibernate-validator换成4版本的
2> 把javax.validation更新到较高版本