万能参数校验框架

简介:结合JSR380,Hibernate validation,生成代理(也可以用Spring AOP来替代)实现的一整套的请求参数校验的框架,后期优化可以实现做成一个服务化的jar包到处使用。本文中将参数校验的所有代码都和业务代码逻辑进行分离,完美契合单一性原则低耦合等设计原则。

全局启用注解:

@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableValidation {

}

具体抽象的业务注解(若干,本文只举例一个):

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PaginationValidator.class)
@Documented
public @interface Pagination {

    String message() default "{com.ctrip.ibu.common.annotation.coreAnnotation.Pagination.message}";

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

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

    int minPageIndex() default 1;

    int minPageSize() default 1;

    int maxPageSize();

}

切入和解析代码

public class ValidationBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("##############################################postProcessBeforeInitialization###############################################");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("##############################################postProcessAfterInitialization###############################################");
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        if (AnnotationUtils.findAnnotation(targetClass, EnableValidation.class) != null) {
            ProxyFactory proxyFactory = new ProxyFactory();
            //proxyFactory.setTarget(bean);
            proxyFactory.setTargetClass(targetClass);
            proxyFactory.addAdvice(new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    //ignore checkHealth()
                    if ("checkHealth".equals(invocation.getMethod().getName())) {
                        // do business logic.
                        Method method = invocation.getMethod();
                        return method.invoke(bean, invocation.getArguments());
//                        return invocation.proceed();
                    } else {
                        Object result = null;
                        try {
                            // Before: check request param
                            doPreHandle(invocation);
                            // do business logic.
                            Method method = invocation.getMethod();
                            result =  method.invoke(bean, invocation.getArguments());
                        } finally {
                            // After: handle the result after finish business logic.
                            doAfterHandle(result);
                        }
                        return result;
                    }
                }
            });
            return proxyFactory.getProxy( ClassUtils.getDefaultClassLoader());
        }
        return bean;
    }





    /**
     * Before: check request param
     * @param invocation
     */
    private void doPreHandle(MethodInvocation invocation) throws Exception {
        Object[] args = invocation.getArguments();
        if (args == null || args.length < 1) {
            throw new Exception("request param size should >=1");
        }
        Object testRequest = args[0];

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();

        Set<ConstraintViolation<Object>> constraintViolationSet = supportMessagePropertity(factory).getValidator().validate(testRequest);
        // Encapsulate the condition that the parameter check is not passed
        if (!CollectionUtils.isEmpty(constraintViolationSet)) {
            List errorList = new ArrayList(constraintViolationSet.size());
            for (Iterator<ConstraintViolation<Object>> constraintViolation = constraintViolationSet.iterator(); constraintViolation.hasNext();) {
                ConstraintViolation error = constraintViolation.next();
                Object errorParam = error.getInvalidValue();
                String errorMessage = error.getMessage();

                errorList.add("[" + String.valueOf(errorParam) + "] error! Error reason: " + errorMessage);
            }
            throw new IllegalRequestParamException(errorList.toString());
        }
        //原生的对Hibernate validation的自己的实现,有兴趣的可以试一下
//                    Field[] declaredFields = testRequest.getClass().getDeclaredFields();
                    for (Field field : declaredFields) {
                        Locale locale =
                                AnnotationUtils.findAnnotation(field, Locale.class);
                        if (locale != null) {
                            Constraint constraint =
                                    AnnotationUtils.findAnnotation(Locale.class, Constraint.class);
                            Class<? extends ConstraintValidator<?, ?>>[] validatorClasses = constraint.validatedBy();
                            for (Class<? extends ConstraintValidator<?, ?>> validatorClass : validatorClasses) {
                                ConstraintValidator validator = BeanUtils.instantiate(validatorClass);

                                validator.isValid(testRequest, null);
                            }
                        }

//                    }
    }


    private HibernateValidatorContext supportMessagePropertity(ValidatorFactory factory){
        return factory.unwrap(HibernateValidatorFactory.class)
                .usingContext().messageInterpolator(new ResourceBundleMessageInterpolator(
                new PlatformResourceBundleLocator("validation-request-params")
        )).failFast(true);
    }


    /**
     * After: handle the result after finish business logic.
     * @param result
     */
    private void doAfterHandle(Object result) {
        // do nothing.


    }

}

若是Spring MVC 方式的请求参数的校验,可以如下配置:

/**
 * Configuration for validation
 */
@Configuration
public class ValidationConfiguration extends WebMvcConfigurerAdapter {
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:validation-request-params");
        return messageSource;
    }

    @Bean
    public LocalValidatorFactoryBean validatorFactoryBean() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        Map<String, String> propertyMap = new HashMap<>(4);
        // fail fast mode
        propertyMap.put(HibernateValidatorConfiguration.FAIL_FAST, "false");
        bean.setValidationPropertyMap(propertyMap);
        bean.setValidationMessageSource(messageSource());
        return bean;
    }

    @Override
    public Validator getValidator() {
        return validatorFactoryBean();
    }
}
private HibernateValidatorContext supportMessagePropertity(ValidatorFactory factory){
        return factory.unwrap(HibernateValidatorFactory.class)
                .usingContext().messageInterpolator(new ResourceBundleMessageInterpolator(
                new PlatformResourceBundleLocator("validation-request-params")
        )).failFast(true);
    }


否则可以使用ValidationBeanPostProcessor类中的supportMessagePropertity的方法来支持。

 

异常类比较简单:

 

public class IllegalRequestParamException extends RuntimeException {
    public IllegalRequestParamException() {
    }

    public IllegalRequestParamException(String message) {
        super(message);
    }

    public IllegalRequestParamException(String message, Throwable cause) {
        super(message, cause);
    }

    public IllegalRequestParamException(Throwable cause) {
        super(cause);
    }
}

支持的自定义的参数校验后的错误提示,更友好,注:可以通过解析的代码中传入locale来实现国际化,即不同语种展示的错误信息自动的转化为对应语言的错误信息:

validation-request-params_zh.properties文件:(value也可以写中文,支持多语言)

com.ctrip.ibu.common.annotation.coreAnnotation.Id.message          = Id role : required must be {required}, min must greater than {min}, max must less than {max}
com.ctrip.ibu.common.annotation.coreAnnotation.Locale.message      = Locale role : required must be {required}, length must be 5
com.ctrip.ibu.common.annotation.coreAnnotation.Pagination.message  = Pagination role : pageSize and pageIndex() can not be null, pageIndex must greater than {minPageIndex}, pageSize must between [{minPageSize}, {maxPageSize}        

注:原创不易,码代码不易,分享精神不易,仅供参考,请勿转载,欢迎讨论。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值