最佳实践_C#Api对接实体校验
工作之后,做过不少的api对接,有提供api接口,也有调用对方api接口。api对接中,实体校验一定是必不可少的一个通用模块。
正常情况下,实体校验通常包括:a 是否必填;b 数据类型;c 最大长度;d 取值范围;e 集合最大数量。
这里记录下自己的最佳实践,并作简单解释。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class DataValidateHelper 2 { 3 public static List<DataValidateResult> Validate<T>(T model) 4 { 5 var result = new List<DataValidateResult>(); 6 var properties = model.GetType().GetProperties(); 7 foreach (var item in properties) 8 { 9 var ats = item.GetCustomAttribute(typeof(DataValidateAttribute), true) as DataValidateAttribute; 10 if (ats == null) 11 continue; 12 13 if (item.PropertyType.Equals(typeof(string))) 14 { 15 var v = Convert.ToString(item.GetValue(model)); 16 if (ats.Required && string.IsNullOrEmpty(v)) 17 { 18 result.Add(new DataValidateResult(item.Name, v, ats.Message)); 19 continue; 20 } 21 22 if (!ats.Required && string.IsNullOrEmpty(v)) 23 continue; 24 25 if (ats.RegexString == null) 26 continue; 27 var regex = new Regex(ats.RegexString); 28 if (regex.IsMatch(v)) 29 continue; 30 31 result.Add(new DataValidateResult(item.Name, v, ats.Message)); 32 continue; 33 } 34 35 if (item.PropertyType.Equals(typeof(List<string>))) 36 { 37 var v = (List<string>)item.GetValue(model); 38 if (ats.Required && (v == null || v.Count == 0)) 39 { 40 result.Add(new DataValidateResult(item.Name, "null", ats.Message)); 41 continue; 42 } 43 44 if (!ats.Required && (v == null || v.Count == 0)) 45 continue; 46 47 if (ats.MaxListCount > 0 && ats.MaxListCount < v.Count) 48 { 49 result.Add(new DataValidateResult(item.Name, v.Count.ToString(), ats.Message)); 50 continue; 51 } 52 53 if (ats.RegexString == null) 54 continue; 55 foreach (var children in v) 56 { 57 var regex = new Regex(ats.RegexString); 58 if (regex.IsMatch(children)) 59 continue; 60 61 result.Add(new DataValidateResult(item.Name, children, ats.Message)); 62 } 63 continue; 64 } 65 66 if (item.PropertyType.IsClass) 67 { 68 if (item.PropertyType.IsGenericType) 69 { 70 var v = item.GetValue(model) as IEnumerable<object>; 71 if (ats.Required && (v == null || v.Count() == 0)) 72 { 73 result.Add(new DataValidateResult(item.Name, "null", ats.Message)); 74 continue; 75 } 76 77 if (!ats.Required && (v == null || v.Count() == 0)) 78 continue; 79 80 if (ats.MaxListCount > 0 && ats.MaxListCount < v.Count()) 81 { 82 result.Add(new DataValidateResult(item.Name, v.Count().ToString(), ats.Message)); 83 continue; 84 } 85 86 foreach (var children in v) 87 { 88 result.AddRange(Validate(children)); 89 } 90 continue; 91 } 92 93 var va = item.GetValue(model); 94 if (ats.Required && va == null) 95 { 96 result.Add(new DataValidateResult(item.Name, "null", ats.Message)); 97 continue; 98 } 99 100 if (!ats.Required && va == null) 101 continue; 102 103 result.AddRange(Validate(va)); 104 } 105 } 106 107 return result; 108 } 109 } 110 111 public class DataValidateResult 112 { 113 public string CoulumnName { get; set; } 114 public string CoulumnValue { get; set; } 115 public string ErroMessage { get; set; } 116 117 public DataValidateResult(string columnName, string coulumnValue, string erroMessage) 118 { 119 CoulumnName = columnName; 120 CoulumnValue = coulumnValue; 121 ErroMessage = erroMessage; 122 } 123 } 124 125 public class DataValidateAttribute : Attribute 126 { 127 public bool Required { get; set; } 128 public string RegexString { get; set; } 129 public int MaxListCount { get; set; } 130 public string Message { get; set; } 131 }
简单解释下
1 实体内字段数据类型共4中:string类型;List<string>类型;T类型;List<T>类型。最小颗粒度当然就是string,避免一些默认值不是null的数据类型,因默认值引起的实际值变化。
2 通过字段特性设置校验,Required:是否必填;MaxListCount:集合最大数量;RegexString:其余校验通过正则表达式体现;Message:自定义校验失败提示。
3 字段校验结果List<DataValidateResult>,所有校验失败的信息在该集合内体现,字段名/当前值/失败提示信息。至于api怎么展现和使用这些校验结果,根绝返回结果自己拼装。
4 整个校验逻辑的主题思想是:通过反射和递归出作为最小颗粒度的每个字段,并将其值和通过特性设置的限制条件对比,如果不符合则视为校验失败,并返回失败信息。