转载

简单的通用后台校验代码

萌生写这个代码的原因,是在使用struts2的验证框架时,总觉的有些不太灵活。当一个action中有多个表单需要处理的时候,struts处理起来就稍微麻烦一点,当校验失败时都会return "input"字符串。但我不同表单单校验失败后一般都希望返回不同的result,渲染不同的视图。另外如果我的校验是异步发起的,而我想要的结果是json的时候,也比较麻烦。虽然这些问题可以通过修改result type类型和在result中使用ognl表达式来做到,但绕来绕去实在太过麻烦。而若不适用这校验框架,在action中自己来if else if else 来判断更不可取。于是便回想到yii和 laravel框架的校验,这些校验框架在我看来做的非常优秀,非常灵活,于是我想借鉴它们来自己写一写这个校验插件。

首先,我希望做到两点

  • 可以对实体进行校验,不管实体封装的是表单数据还是数据库记录
  • 可以直接对request.getParameterMap()获得的map进行校验,这个我暂时还没完成。
  • 灵活校验,当校验失败后可以马上通过getErrors拿到错误数据,这样你想咋么响应怎么响应,发json也好,把errors传递到视图也好,都可以。
    • 也就是想下面这样,if(!entity.validate()){....}else{Map map = entity.getErrors()}

另外当实体的属性没有被struts填充时,也可以使用我写的一个fill方法进行填充,为什么要写这个呢,因为struts的属性驱动, 类驱动,模型驱动都有点局限,但我的action里有多个模型,模型驱动只能为一个模型注值,不够灵活

 1 public class BaseEntity{  2     public void fill(Map<String, String[]> parameterMap) {  3         //System.out.println(this.getClass());  4         Class clazz = this.getClass();  5         Field fields[] = clazz.getDeclaredFields();  6         for (int i = 0; i < fields.length; i++) {  7             String fieldName = fields[i].getName();  8             //System.out.println(fieldName);  9             try { 10                 // 查找参数集合 11                 String values [] = parameterMap.get(fieldName); 12                 if(values!=null && values.length>0){ 13                     String methodName = "set"+fieldName.substring(0, 1).toUpperCase() 14                         + fieldName.substring(1); 15                     Class fieldType = fields[i].getType(); 16                     Method method = clazz.getMethod(methodName,new Class[]{fieldType}); 17                     // 设值 18                     method.invoke(this,new Object[]{fieldType.cast(values[0])}); 19                 } 20             } catch (Exception e) { 21                 e.printStackTrace(); 22             } 23         } 24     } 25     ///... 26 }

我把代码放在了 svnchina 和 github 上,有兴趣看看的可以去,有一起做的更好了。

  • http://www.svnchina.com/svn/entity_validator
  • https://github.com/lvyahui8/validator.git

我暂时只做了对实体的校验,这里还有个问题,java本身是强类型的语言。对于实体的某些字段,他的类型本身就存在了校验,比如一个字段是Integer类型,那你觉得还有必要去校验他是number类型吗?完全多次一举。只有当属性是字符串,校验它是否是number才有意义,比如手机号码

这里我写了一个BaseEntity类, 如果一个子类要进行校验,那么这个子类要覆写父类的两个方法,rules和labels方法。这两个方法,一个用来定义对该试题的校验规则,一个定义对实体字段的中文翻译。

 1 package org.lyh.validator;  2   3 import java.lang.reflect.Field;  4 import java.lang.reflect.Method;  5 import java.util.HashMap;  6 import java.util.Map;  7   8 /**  9  * Created by lvyahui on 2015-06-27. 10  */ 11 public class BaseEntity { 12  13     private Validator validator = new Validator(this); 14  15     protected Map<String,String> labelMap = new HashMap<String,String>(); 16  17     { 18         String [][] allLabels = labels(); 19         if(allLabels != null){ 20             for(String [] label : allLabels){ 21                 String prop = label[0],propLabel = label[1]; 22                 if(prop != null && hasProp(prop) && propLabel != null){ 23                     labelMap.put(prop,propLabel); 24                 } 25             } 26         } 27     } 28  29     public boolean hasProp(String prop) { 30         try { 31             Field field = this.getClass().getDeclaredField(prop); 32             return true; 33         } catch (NoSuchFieldException e) { 34             return false; 35         } 36     } 37  38     public String getLabel(String prop){ 39         return labelMap.get(prop); 40     } 41  42     public void fill(Map<String, String[]> parameterMap) { 43         //System.out.println(this.getClass()); 44         Class clazz = this.getClass(); 45         Field fields[] = clazz.getDeclaredFields(); 46         for (int i = 0; i < fields.length; i++) { 47             String fieldName = fields[i].getName(); 48             //System.out.println(fieldName); 49             try { 50                 // 查找参数集合 51                 String values [] = parameterMap.get(fieldName); 52                 if(values!=null && values.length>0){ 53                     String methodName = "set"+fieldName.substring(0, 1).toUpperCase() 54                         + fieldName.substring(1); 55                     Class fieldType = fields[i].getType(); 56                     Method method = clazz.getMethod(methodName,new Class[]{fieldType}); 57                     // 设值 58                     method.invoke(this,new Object[]{fieldType.cast(values[0])}); 59                 } 60             } catch (Exception e) { 61                 e.printStackTrace(); 62             } 63         } 64     } 65      66     /** 67      * 进行校验 68      * @return 校验 69      */ 70     public boolean validate() { 71         return this.validator.validate(); 72     } 73  74     /** 75      * 如果校验失败通过他获取错误信息 76      * @return 错误信息 77      */ 78     public Map<String,Map<String,String>> getErrors(){ 79         return this.validator.getErrors(); 80     } 81  82     /** 83      * 校验规则 84      * @return 校验规则 85      */ 86  87     public String[][] labels(){return null;} 88  89     /** 90      * 字段翻译 91      * @return 字段翻译 92      */ 93     public String [][] rules(){return null;} 94      95 }

下面创建一个子类,重写rules和labels方法。

 1 package org.lyh.validator;  2   3 import java.sql.Timestamp;  4   5 /**  6  * Created by lvyahui on 2015-06-26.  7  */  8   9 public class UserEntity extends BaseEntity { 10     private String username; 11     private String password; 12     private String name; 13     private Integer gold; 14     private Integer progress; 15     private Timestamp createdAt; 16     private String email; 17     private String phone; 18     private String site; 19     /*省略 getter/setter方法*/ 20     @Override 21     public String[][] rules() { 22         return new String [][] { 23                 {"username,password,email,gold,progress,phone","required"}, 24                 {"username,password","length","6","14"}, 25                 {"rePassword","equals","password"}, 26                 {"email","email","//w{6,12}"}, 27                 {"createdAt","timestamp"}, 28                 {"phone","number"}, 29                 {"site","url"} 30         }; 31     } 32  33     public String[][] labels() { 34         return new String[][]{ 35                 {"username","用户名"}, 36                 {"password","密码"}, 37                 {"rePassword","确认密码"}, 38                 {"email","邮箱"}, 39                 {"progress","进度"}, 40                 {"phone","电话"}, 41                 {"gold","金币"} 42         }; 43     } 44  45 }

可以看到,校验规则的写法,很简单,每一行第一个字符串写了需要校验的字段,第二个字符串写了这些字段应该满足的规则,后面的字符串是这个规则需要的参数

labels 就更简单了。

另外这个实例化validator类的时候,除了传递实体外,还可以传递第二个参数,表示是否是短路校验。

下面是这个校验的核心类Validator

  1 package org.lyh.validator;   2    3 import java.lang.reflect.Field;   4 import java.lang.reflect.Method;   5 import java.sql.Timestamp;   6 import java.text.MessageFormat;   7 import java.util.*;   8    9 /**  10  * Created by lvyahui on 2015-06-27.  11  *  12  */  13 public class Validator {  14     /**  15      * 校验类型  16      */  17     public static final String required = "required";  18     public static final String length = "length";  19     public static final String number = "number";  20     public static final String equals = "equals";  21     public static final String email = "email";  22     public static final String url = "url";  23     public static final String regex = "regex";  24     public static final String timestamp = "timestamp";  25   26     /**  27      * 附加参数类型  28      */  29     private static final int PATTERN = 1;  30     private static final int MIN = 2;  31     private static final int MAX = 3;  32     private static final int PROP = 4;  33   34     private Map<Integer,Object> params = new HashMap<Integer,Object>();  35     /**  36      * 验证失败的错误信息,形式如下  37      * {"username":{"REQUIRED":"用户名必须为空",...},...}  38      */  39     protected Map<String,Map<String,String>> errors  40         = new HashMap<String,Map<String,String>>();  41   42     /**  43      * 被校验的实体  44      */  45     private BaseEntity baseEntity;  46   47     /**  48      * 被校验实体的类型  49      */  50     private Class entityClass = null;  51   52     /**  53      * 当前正在被校验的字段  54      */  55     private Field field;  56     /**  57      * 当前执行的校验  58      */  59     private String validateType ;  60   61     /**  62      * 当前被校验字段的值  63      */  64     private Object value;  65   66     /**  67      * 是否短路  68      */  69     private boolean direct;  70   71     private String [][] rules ;  72   73     /**  74      *  75      * @param baseEntity  76      */  77     public Validator(BaseEntity baseEntity) {  78         this(baseEntity,false);  79     }  80   81     public Validator(BaseEntity baseEntity,boolean direct){  82         this.baseEntity = baseEntity;  83         entityClass = baseEntity.getClass();  84         rules = baseEntity.rules();  85     }  86   87     public Map<String,Map<String,String>> getErrors() {  88         return errors;  89     }  90   91     public Map<String,String> getError(String prop){  92         return this.errors.get(prop);  93     }  94   95     public void addError(String prop,String validatorType,String message){  96         Map<String,String> error = this.errors.get(prop);  97         if(error==null || error.size() == 0){  98             error = new HashMap<String,String>();  99             errors.put(prop, error); 100         } 101         error.put(validatorType,message); 102     } 103  104     public void setRules(String[][] rules) { 105         this.rules = rules; 106     } 107  108     public boolean required(){ 109         if(value!=null){ 110             if(value.getClass() == String.class && "".equals(((String)value).trim())){ 111                 return false; 112             } 113             return true; 114         } 115         return false; 116     } 117  118     public boolean number(){ 119         if(value.getClass().getGenericSuperclass() == Number.class){ 120             return true; 121         }else if(((String)value).matches("^//d+$")){ 122             return true; 123         } 124         return false; 125     } 126  127     public boolean email(){ 128         return ((String) value).matches("^//w+@//w+.//w+.*//w*$"); 129     } 130  131     public boolean url(){ 132         return ((String)value).matches("^([a-zA-Z]*://)?([//w-]+//.)+[//w-]+(/[//w//-//.]+)*[///?%&=]*$"); 133     } 134  135     public boolean regex(){ 136         return ((String)value).matches((String) params.get(regex)); 137     } 138  139     public boolean equals() throws NoSuchFieldException, IllegalAccessException { 140         String prop = (String) params.get(PROP); 141         Field equalsField = entityClass.getDeclaredField(prop); 142         equalsField.setAccessible(true); 143         return value.equals(equalsField.get(baseEntity)); 144     } 145     public boolean timestamp(){ 146         if(field.getType().equals(Timestamp.class)){ 147             return true; 148         } 149         return false; 150     } 151     public boolean length() { 152         String val = (String) value; 153         Integer min = (Integer) params.get(MIN); 154         Integer max = (Integer) params.get(MAX); 155  156         return val.length() > min && (max == null || val.length() < max); 157  158  159     } 160  161     public boolean validate(){ 162         errors.clear(); 163         if(rules==null){ 164             return true; 165         } 166  167         for (int i = 0; i < rules.length; i++) { 168             String [] rule = rules[i],fields = rule[0].split(","); 169             validateType = rule[1]; 170             setParams(rule); 171             try { 172                 Method validateMethod = this.getClass() 173                     .getMethod(validateType); 174                 for (int j = 0; j < fields.length; j++) { 175                     if(direct && getError(fields[j]) != null){ continue; } 176  177                     field = entityClass.getDeclaredField(fields[j]); 178                     field.setAccessible(true); 179                     value = field.get(baseEntity); 180                     if(value != null || (value == null && validateType == required)){ 181                         if(!(Boolean)validateMethod.invoke(this)){ 182                             handleError(); 183                         } 184                     } 185                 } 186             } catch (Exception e) { 187                 e.printStackTrace(); 188             } 189         } 190         return true; 191     } 192  193     private void setParams(String [] rule) { 194         params.clear(); 195         // 取附加参数 196         switch (validateType){ 197             case regex: 198                 params.put(PATTERN,rule[3]); 199                 break; 200             case equals: 201                 params.put(PROP, rule[2]); 202                 break; 203             case length: 204                 if(rule[2] != null) { 205                     params.put(MIN, Integer.valueOf(rule[2])); 206                 } 207                 if(rule.length >= 4){ 208                     params.put(MAX,Integer.valueOf(rule[3])); 209                 } 210                 break; 211             default: 212         } 213     } 214  215     private void handleError() { 216         String name = this.baseEntity.getLabel(field.getName()) != null 217                 ? this.baseEntity.getLabel(field.getName()) : field.getName(), 218                 message = MessageFormat.format(Messages.getMsg(validateType),name); 219         this.addError(field.getName(), validateType, message); 220     } 221  222 }

下面是错误消息模板

 1 package org.lyh.validator;  2   3 import java.util.HashMap;  4 import java.util.Map;  5   6 /**  7  * Created by lvyahui on 2015-06-28.  8  */  9 public class Messages { 10     private static Map<String,String> msgMap = new HashMap<String,String>(); 11  12     static{ 13         msgMap.put(Validator.required,"{0}必须填写"); 14         msgMap.put(Validator.email,"{0}不合法"); 15         msgMap.put(Validator.equals,"{0}与{1}不相同"); 16         msgMap.put(Validator.length,"{0}长度必须在{1}-{2}之间"); 17         msgMap.put(Validator.regex,"{0}不符合表达式"); 18         msgMap.put(Validator.timestamp,"{0}不是合法的日期格式"); 19         msgMap.put(Validator.number,"{0}不是数字格式"); 20         msgMap.put(Validator.url,"{0}不是合法的url"); 21     } 22  23     public static String getMsg(String validateType) { 24         return msgMap.get(validateType); 25     } 26 }

下面写个main方法测试

 1 package org.lyh.validator;  2   3 /**  4  * Created by lvyahui on 2015-06-28.  5  */  6 public class MainTest {  7     public static void main(String[] args) {  8   9         UserEntity userEntity = new UserEntity(); 10         userEntity.validate(); 11         System.out.println(userEntity.getErrors()); 12  13         userEntity.setUsername("lvyahui"); 14         userEntity.setRePassword("admin888"); 15         userEntity.setPassword("admin888"); 16         userEntity.setEmail("lvyaui82.com"); 17         userEntity.validate(); 18         System.out.println(userEntity.getErrors()); 19  20         userEntity.setEmail("lvyaui8@12.com"); 21         userEntity.setPhone("hjhjkhj7867868"); 22         userEntity.setGold(1); 23         userEntity.setSite("www.baidu.com"); 24  25         userEntity.validate(); 26  27         System.out.println(userEntity.getErrors()); 28         // ([a-zA-Z]*://)?([/w-]+/.)+[/w-]+(/[/w-]+)*[/?%&=]* 29         userEntity.setSite("http://www.baidu.com/index.php"); 30         userEntity.setProgress(123); 31         userEntity.setPhone("156464564512"); 32         userEntity.validate(); 33  34         System.out.println(userEntity.getErrors()); 35  36     } 37  38 }

运行程序,得到这样的输出

1 {gold={required=金币必须填写}, password={required=密码必须填写}, phone={required=电话必须填写}, progress={required=进度必须填写}, email={required=邮箱必须填写}, username={required=用户名必须填写}} 2 {gold={required=金币必须填写}, phone={required=电话必须填写}, progress={required=进度必须填写}, email={email=邮箱不合法}} 3 {phone={number=电话不是数字格式}, progress={required=进度必须填写}} 4 {}

后面要做的就是直接对map进行校验

我的想法是通过validator类添加set方法注入规则和翻译。当然上面的错误信息模板可以到xml文件中进行配置,至于校验规则和翻译个人认为没有必要放到xml中去。

正文到此结束
Loading...