jfinal的Validator的错误消息想按顺序只取第一个验证的失败信息(自己刚解决顺便分享代码)

错觉 发布于 2016/03/31 21:15
阅读 914
收藏 1

之前写后端验证的时候都是在controller里面进行,每个字段都是这么判断

//判断用户名是否为空
        if (Strings.isNullOrEmpty(username)) {
            rendFastJson(JsonResult.build(ErrCode.USERNAME_EMPTY));
            return;
        }

        //判断用户名是否为空
        if (Strings.isNullOrEmpty(password)) {
            rendFastJson(JsonResult.build(ErrCode.PASSWORD_EMPTY));
            return;
        }



这样哪个先验证失败,那么前端获取的就是第一个出错的信息,用专业词汇叫“快速短路",现在我不想这么弄了,我想用JFinal的Validation机制来实现,为了让controller中更清爽一些,我建立一个Validitor,如下

@Override
    protected void validate(Controller c) {
        validateRequiredString(username, msgKey(username), "用户名不能为空");
        validateRequiredString(password, msgKey(password), "密码不能为空");
        validateMobile(username,msgKey(username),"用户不是合法手机号");
        validateEmail(username,msgKey(username),"用户名不是合法邮箱格式");
    }

    @Override
    protected void handleError(Controller c) {
        //获取第一个验证错误详情
        String firstErrMsg = getFirstErrMsg(c);

        c.renderJson("{'errcmsg':'" + firstErrMsg + "'}");
    }



我发现我获取不到第一个错误了,因为jFinal将错误信息利用setAttr进行的设置,而setAttr后的结果是基于Map的因此就无序了

===============以下为解决方案====================

看Validator#addError的源码得知,JFinal也是支持快速短路的,哈哈哈哈,其源码如下:

protected void addError(String errorKey, String errorMessage) {
		invalid = true;
		controller.setAttr(errorKey, errorMessage);
		if (shortCircuit) {
			throw new ValidateException();
		}
	}



只要把shortCircuit设置为true就支持了哈哈

所以代码改为如下就实现了,顺便整理了下代码,完整代码如下:

BaseValidator.java

package com.app.common.web;

import com.jfinal.core.Controller;
import com.jfinal.validate.Validator;

import java.util.Enumeration;

/**
 * Created by yc on 16/3/31.
 * 自定义后端验证基类
 */
public class BaseValidator extends Validator {
    //校验手机号码正则表达式
    private static final String mobilePattern = "\\b(1[3,4,5,7,8,9]\\d{9})\\b";

    //错误key后缀
    protected static final String ERROR_KEY = ":errorkey";

    //方便错误消息key的命名
    public String msgKey(String msg) {
        return msg + ERROR_KEY;
    }

    /**
     * 获取Validator过程中第一个错误信息
     * 后端验证主要为了验证非空以及处理绕过前端验证的非法请求
     * jfinal的Validator会将错误利用setAttr进行设置
     *
     * @param c
     * @return 第一个错误信息
     */
    public static String getFirstErrMsg(Controller c) {
        String findErrorAttrName = "";
        Enumeration<String> attrNames = c.getAttrNames();

        //获取第一个错误属性key
        if (attrNames != null) {
            while (attrNames.hasMoreElements()) {
                String attrName = attrNames.nextElement();
                if (attrName.lastIndexOf(ERROR_KEY) > 0) {
                    findErrorAttrName = attrName;
                    break;
                }
            }
        }
        //获取第一个验证错误详情
        String errMsg = c.getAttr(findErrorAttrName);

        return errMsg;
    }

    /**
     * 验证是否合法手机号或邮箱
     * 一般用于验证用户名
     *
     * @param field
     * @param errorKey
     * @param errorMsg
     */
    protected void validateMobileAndEmail(String field, String errorKey, String errorMsg) {
        validateRegex(field, mobilePattern, false, errorKey, errorMsg);
        validateEmail(field, errorKey, errorMsg);
    }

    /**
     * 验证是否合法手机号
     *
     * @param field
     * @param errorKey
     * @param errorMsg
     */
    protected void validateMobile(String field, String errorKey, String errorMsg) {
        validateRegex(field, mobilePattern, false, errorKey, errorMsg);
    }

    @Override
    protected void validate(Controller c) {
        //启用快速失败
        setShortCircuit(true);
        //这里无需进行实现
    }

    @Override
    protected void handleError(Controller c) {
        //获取第一个验证错误详情
        String firstErrMsg = getFirstErrMsg(c);

        c.renderJson("{'errcmsg':'" + firstErrMsg + "'}");
    }
}



登录验证类

package com.app.sign.validator;

import com.app.common.web.BaseValidator;
import com.jfinal.core.Controller;

/**
 * Created by yc on 16/3/31.
 * 登录系统验证用户名和口令
 */
public class SignInAuthValidator extends BaseValidator {
    private String username = "username";
    private String password = "password";

    @Override
    protected void validate(Controller c) {
        //实现父类的验证
        super.validate(c);

        //声明自身的验证
        validateRequiredString(username, msgKey(username), "用户名不能为空");
        validateRequiredString(password, msgKey(password), "密码不能为空");
        validateMobileAndEmail(username, msgKey(username), "用户名不是合法手机号或邮箱");
    }
}



一下清爽了很多,爽~

加载中
2
如梦技术
如梦技术
可以看下我这个:ShortCircuitValidator:http://git.oschina.net/596392912/JFinal-commons/blob/master/src/main/java/com/siweifu/base/ShortCircuitValidator.java 需要短路校验的,继承它就好了。
如梦技术
如梦技术
回复 @错觉 : 大家好才是真的好
错觉
错觉
你这个好 哈哈 加到我自己代码里面了 谢啦
0
JFinal
JFinal
   最简单快速的方法是在需要使用短路校验的XxxxValidator中,在调用 validateXxxx(...) 方法前先调用一下 this.setShortCircuit(true),感谢分享  
JFinal
JFinal
回复 @贝宝明 : jfinal比较特殊,因为代码量极少,只有 9000 行左右,对于 jfinal 来说其实看源码比看文档要简单快速得多,代码都很简单直接,通常就是对下层的 servlet/jdbc 的一个简单的转调就打完收工了
贝宝明
贝宝明
回复 @JFinal : 不能指望所有的开发者在遇到问题时都去阅读源代码,JFinal要想发展的好,还得想spring一样有详细的文档
Jim_Ai
Jim_Ai
手动点赞。
JFinal
JFinal
回复 @贝宝明 : jfinal 手册中提到的功能只是很少的一部分,还有很多隐藏功能有待开发者从源码中探索到
贝宝明
贝宝明
估计是文档里没有详细描述,都得看源代码才能知道还有一个shortCircuit。
返回顶部
顶部