另类好用的JFinal参数验证机制

在JFinal中,如果我们要做数据验证,需要添加添加@Before(YourValidator.class),有没有更好用的方案呢?

在Jboot的方案中,使用的验证模式如下:例如非空验证,我们可以编写自己的一个注解,例如:

package io.jboot.web.controller.annotation;

import java.lang.annotation.*;


@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface EmptyParaValidate {

    String[] value();

    String errorRedirect() default "";

}


然后在Controller的某个action来使用的时候,直接如下使用:

public class MyController extend JbootController{
    
    @EmptyParaValidate("name")
    public void index(){
    
    }
}


这样,就可以对index()这个action的name参数进行验证。


使用是非常简单,那么怎么来实现这个@EmptyParaValidate注解的功能呢?很简单,编写一个拦截器,然后添加到全局拦截器里,拦截器代码如下:

public class ParaValidateInterceptor implements Interceptor {

    public static final int DEFAULT_ERROR_CODE = 99;

    @Override
    public void intercept(Invocation inv) {

        EmptyParaValidate emptyParaValidate = inv.getMethod().getAnnotation(EmptyParaValidate.class);
        if (emptyParaValidate == null) {
            inv.invoke();
            return;
        }

        String[] paraKeys = emptyParaValidate.value();
        if (ArrayUtils.isNullOrEmpty(paraKeys)) {
            inv.invoke();
            return;
        }

        for (String param : paraKeys) {
            String value = inv.getController().getPara(param);
            if (value == null || value.trim().length() == 0) {
                renderError(inv, param, emptyParaValidate.errorRedirect());
                return;
            }
        }

        inv.invoke();
    }


    private void renderError(Invocation inv, String param, String errorRedirect) {
        if (StringUtils.isNotBlank(errorRedirect)) {
            inv.getController().redirect(errorRedirect);
            return;
        }

        //如果ajax请求,返回一个错误数据。
        if (RequestUtils.isAjaxRequest(inv.getController().getRequest())) {
            inv.getController().renderJson(Ret.fail("msg", "数据不能为空").set("errorCode", DEFAULT_ERROR_CODE).set("field", param));
            return;
        }

        inv.getController().renderError(404);
    }


}


这样,就随时随地通过 @EmptyParaValidate 注解来对任何方法进行数据验证了,同时参考 @EmptyParaValidate 的实现,我们可以扩展出 @EmailParaValidate等等其他的注解功能。

具体代码参考:https://gitee.com/fuhai/jboot/blob/master/src/main/java/io/jboot/web/controller/validate/ParaValidateInterceptor.java


你有其他更好的方案吗?可以留言交流。

评论区

JFinal

2017-10-13 16:45

这个方案已经很简洁了,很适合将一些常用的验证定制成注解,用的时候像点菜一样,点赞

JFinal

2017-10-13 16:48

还有一点,这个方案性能非常好,因为拦截器就是一个普通的方法调用,不涉及反射,ParaValidateInterceptor 全局拦截毫无压力

海哥

2017-10-13 16:51

@JFinal 老大点赞,就是最大的肯定。

JFinal

2017-10-13 16:59

@海哥 代码简洁,功能实用

lyh061619

2017-10-14 12:11

海哥你这个用得精啊,直接基于拦截器实现注解参数验证,使用、性能两不误。

lyh061619

2017-10-14 12:15

直接体现JFinal框架的设计精湛与灵活性,使用体验想怎么舒服就怎么来。^_^!!

Dreamlu

2017-10-14 23:05

@海哥 我试了下,jfinal-java8集成hibernate-validator也挺方便的,个人觉得hibernate-validator这种校验表单form还是要方便些。

JFinal

2017-10-14 23:19

@Dreamlu 在我的实践中,不正确的数据在 service 层就给清洗干净了,调用 service 层API 时不符合要求的数据直接就被 return 回来了。更严格的地方在 controller 层的 validator 上也清理过一次

而在 ORM 这一层就没有多少必要再做一次 validate 了,一来是因为 controller、service 层做过一次了,再做一次显得多余了

二来是在创建数据表的时候,为表的字段设置一个很严格的规则,这样也能在很大程度上杜绝错误的数据进入数据库,例如字段值不能为 null,字段长度不能超出范围

如果软件是以 service 为核心去设计,那么数据到达 service 时确保正确,才让业务层走正常流程

Dreamlu

2017-10-16 09:52

@JFinal 是在controller上做校验,jfinal-java8的控制器方法上public void save(@Valid AddressForm form);然后AddressForm上一系列的校验注解!

Dreamlu

2017-10-16 09:53

@JFinal 名字虽然叫hibernate-validator,但是和hibernate没啥关系,就跟javascript和java一样

JFinal

2017-10-16 10:20

@Dreamlu 原来是这么回事,一直以为 hibernate-validator 是在 orm 层搞验证,涨知识了 ^_^

a铖

2017-12-05 00:16

@Dreamlu jdk1.7环境下也能用JSR 303 Bean Validation,引入

javax.validation
validation-api
1.1.0.Final


org.hibernate
hibernate-validator
5.1.1.Final

为param bean加上注解,重写getbean对bean做校验,不合格时返回相应信息。我还是习惯使用JSR 303,有现成的一套api可用

a铖

2017-12-05 00:51

@JFinal 感觉使用JSR303 Bean Validation比写Jfinal Validator方便许多,可以在bean里面指定通用验证规范注解,能否考虑jfinal加入这样的验证机制,我的应用过程 http://www.jfinal.com/share/511 。愿jfinal越来越好