用户输入都是不可信的,我想,大多数后端开发人员,都有这么一个共识。
然后,在写每一个方法的时候,基本都会有一坨if校验,我也不例外。
代码写多了,有时候就会想,能不能优化下,能不能优雅点?
于是,我又开始寻寻觅觅...
这个,就是,声明式验证...
以下是我整理的代码:
声明式验证核心部分:
1 public enum ValidateType 2 { 3 IsString = 10000, 4 IsInt32 = 20000, 5 IsDecimal = 30000, 6 IsDateTime = 40000, 7 IsBoolean = 50000, 8 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 [AttributeUsage(AttributeTargets.All)] 2 public class ValidateAttribute : Attribute 3 { 4 //是否必填 5 private bool isRequired = true; 6 //最小长度 7 private int minLength = 0; 8 //最大长度 9 private int maxLength = Int32.MaxValue; 10 //字段指定长度 11 private int stringLength = Int32.MaxValue; 12 //验证类型 13 private ValidateType validateType; 14 15 /// <summary> 16 /// 显示名称 17 /// </summary> 18 public string DisplayName { get; set; } 19 20 /// <summary> 21 /// 是否必填 22 /// </summary> 23 public bool IsRequired 24 { 25 get 26 { 27 return isRequired; 28 } 29 set 30 { 31 isRequired = value; 32 } 33 } 34 35 /// <summary> 36 /// 最小长度 37 /// </summary> 38 public int MinLength 39 { 40 get 41 { 42 return minLength; 43 } 44 set 45 { 46 minLength = value; 47 } 48 } 49 50 /// <summary> 51 /// 最大长度 52 /// </summary> 53 public int MaxLength 54 { 55 get 56 { 57 return maxLength; 58 } 59 set 60 { 61 maxLength = value; 62 } 63 } 64 65 /// <summary> 66 /// 字段指定长度 67 /// </summary> 68 public int StringLength 69 { 70 get 71 { 72 return stringLength; 73 } 74 set 75 { 76 stringLength = value; 77 } 78 } 79 80 /// <summary> 81 /// 正则表达式校验格式 82 /// </summary> 83 public string RegexpCheckFormat { get; set; } 84 85 /// <summary> 86 /// 正则表达式显示格式 87 /// </summary> 88 public string RegexpDisplayFormat { get; set; } 89 90 /// <summary> 91 /// Int32集合 92 /// </summary> 93 public int[] Int32Collection { get; set; } 94 95 /// <summary> 96 /// 字符串集合 97 /// </summary> 98 public string[] StringCollection { get; set; } 99 100 /// <summary> 101 /// 验证类型 102 /// </summary> 103 public ValidateType ValidateType 104 { 105 get 106 { 107 return validateType; 108 } 109 set 110 { 111 validateType = value; 112 } 113 } 114 115 /// <summary> 116 /// 构造函数 117 /// </summary> 118 /// <param name="validateType"></param> 119 public ValidateAttribute(ValidateType validateType) 120 { 121 this.validateType = validateType; 122 } 123 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static class ValidateHandler 2 { 3 /// <summary> 4 /// 获得结果 5 /// </summary> 6 /// <param name="instance">类对象实例</param> 7 /// <returns></returns> 8 public static string GetResultString(object instance) 9 { 10 var result = string.Empty; 11 if (instance.IsNull()) 12 { 13 return "instance 参数不能为空"; 14 } 15 Type type = instance.GetType(); 16 PropertyInfo[] properties = type.GetProperties(); 17 foreach (PropertyInfo property in properties) 18 { 19 //获取验证特性 20 object[] validAttrs = property.GetCustomAttributes(typeof(ValidateAttribute), true); 21 if (!validAttrs.IsNull()) 22 { 23 //获取属性的值 24 object propVal = property.GetValue(instance, null); 25 //属性值长度 26 int propValLength = 0; 27 foreach (ValidateAttribute validAttr in validAttrs) 28 { 29 //获取属性名称 30 var propName = !validAttr.DisplayName.IsNullOrEmptyOrWhiteSpace() ? validAttr.DisplayName : property.Name; 31 //校验是否必填 32 if (validAttr.IsRequired) 33 { 34 if (propVal.IsNull()) 35 { 36 return "{0} 字段是必需的".FormatWith(propName); 37 } 38 if (validAttr.ValidateType == ValidateType.IsString 39 && propVal.ToString().IsNullOrEmptyOrWhiteSpace()) 40 { 41 return "{0} 字段是必需的".FormatWith(propName); 42 } 43 if (validAttr.ValidateType == ValidateType.IsInt32 44 && !propVal.IsInt32()) 45 { 46 return "{0} 字段不是Int32类型".FormatWith(propName); 47 } 48 if (validAttr.ValidateType == ValidateType.IsDecimal 49 && !propVal.IsDecimal()) 50 { 51 return "{0} 字段不是Decimal类型".FormatWith(propName); 52 } 53 if (validAttr.ValidateType == ValidateType.IsDateTime) 54 { 55 if (!propVal.IsDateTime()) 56 { 57 return "{0} 字段不是日期类型".FormatWith(propName); 58 } 59 if (propVal.ToDateTime() == DateTime.MinValue) 60 { 61 return "{0} 字段是必需的".FormatWith(propName); 62 } 63 } 64 if (validAttr.ValidateType == ValidateType.IsBoolean 65 && !propVal.IsBoolean()) 66 { 67 return "{0} 字段不是布尔类型".FormatWith(propName); 68 } 69 } 70 propValLength = propVal.IsNull() ? 0 : propVal.ToString().Length; 71 //校验最小长度 72 if (propValLength < validAttr.MinLength) 73 { 74 return "{0} 字段或数组长度不能小于{1}".FormatWith(propName, validAttr.MinLength); 75 } 76 //校验最大长度 77 if (propValLength > validAttr.MaxLength) 78 { 79 return "{0} 字段或数组长度不能大于{1}".FormatWith(propName, validAttr.MaxLength); 80 } 81 if (validAttr.StringLength != Int32.MaxValue 82 && propValLength != validAttr.StringLength) 83 { 84 return "{0} 字段长度错误,长度必须为{1}".FormatWith(propName, validAttr.StringLength); 85 } 86 //校验正则表达式 87 if (!validAttr.RegexpCheckFormat.IsNullOrEmptyOrWhiteSpace()) 88 { 89 if (!Regex.IsMatch(propVal.ToString(), validAttr.RegexpCheckFormat)) 90 { 91 if (!validAttr.RegexpDisplayFormat.IsNull()) 92 { 93 return "{0} 字段格式错误,正确格式为{1}".FormatWith(propName, validAttr.RegexpDisplayFormat); 94 } 95 else 96 { 97 return "{0} 字段格式错误".FormatWith(propName); 98 } 99 } 100 } 101 //校验集合 102 if (propVal.IsInt32() 103 && !validAttr.Int32Collection.IsNull() 104 && !validAttr.Int32Collection.Contains(propVal.ToInt32())) 105 { 106 return "{0} 字段必须为 {1} 中的一个".FormatWith(propName, string.Join(",", validAttr.Int32Collection)); 107 } 108 if (!validAttr.StringCollection.IsNull() 109 && !validAttr.StringCollection.Contains(propVal.ToString())) 110 { 111 return "{0} 字段必须为 {1} 中的一个".FormatWith(propName, string.Join(",", validAttr.Int32Collection)); 112 } 113 } 114 } 115 } 116 return string.Empty; 117 } 118 }
有一个需要验证的资源类:
1 public class ResourceDto 2 { 3 [Validate(ValidateType.IsDateTime, IsRequired = false)] 4 public string ResoCreateTime { get; set; } 5 6 [Validate(ValidateType.IsString, IsRequired = false)] 7 public string ResoDesc { get; set; } 8 9 [Validate(ValidateType.IsString, IsRequired = false)] 10 public string ResoId { get; set; } 11 12 [Validate(ValidateType.IsBoolean, DisplayName = "是否显示")] 13 public string ResoIsShow { get; set; } 14 15 [Validate(ValidateType.IsString, DisplayName = "资源名称")] 16 public string ResoName { get; set; } 17 18 [Validate(ValidateType.IsInt32, DisplayName = "资源排序")] 19 public string ResoOrder { get; set; } 20 21 [Validate(ValidateType.IsString, DisplayName = "父资源ID")] 22 public string ResoParentId { get; set; } 23 24 [Validate(ValidateType.IsString, DisplayName = "资源类型")] 25 public string ResoType { get; set; } 26 27 [Validate(ValidateType.IsDateTime, IsRequired = false)] 28 public string ResoUpdateTime { get; set; } 29 30 [Validate(ValidateType.IsString, IsRequired = false)] 31 public string ResoUrl { get; set; } 32 }
控制器保存数据校验部分:
1 public void ResourceSave() 2 { 3 //... 4 ResourceDto model = new JavaScriptSerializer().Deserialize<ResourceDto>("FormData".ValueOfForm()); 5 var checkResult = ValidateHandler.GetResultString(model); 6 if (checkResult != string.Empty) 7 { 8 //以下是处理错误的代码 9 return; 10 } 11 //... 12 }
使用声明式验证,代码有没有简洁美观点?哈哈。