前言
在方法中,为了保证数据的有效性,难免对它做检查,在参数多的时候,if判断就会很多。示例代码如下:
public ActionResult Login(string uid, string pwd) { ResultDTO rdto = new ResultDTO(); if (uid == null || uid.Length == 0) { rdto.Message = "用户名不能为空"; return this.Json(rdto); } if (pwd == null || pwd.Length == 0) { rdto.Message = "密码不能为空"; return this.Json(rdto); } ... }
这仅仅是2个字段进行非空验证,如果是字段多,而且验证方式更多,则难免也写N个if判断。
MVC中提供了验证机制,在实体上添加Attrbute即可指明验证参数的方法。但是很多时候参数是不固定的呀,多变的,比如说查询条件,少则三四个,多则十几个,更多的也有。所以在验证参数这一块,还不知道有什么方法可以简化我们的工作量,如果有朋友知道,还请告知,先谢过。
来说说楼主的方法,目前仅在猜想中,未应用于任何项目。
初步方案
先看看楼主的调用方式:
using System;using System.Collections.Generic;using System.Collections.Specialized;using System.Diagnostics;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Web;namespace MacRead{ class Program { static void Main(string[] args) { //参数格式:模仿MVC中的FormValueCollection,或Request["xxx"] var form = new NameValueCollection(); form["UserName"] = "admin"; form["password"] = "*********"; form["Sex"] = "2"; //form["Sex"] = null; form["Email"] = "304885433@qq.com"; /* 指明必须验证的字段 验证数据 自动填充Model */ var Request = HttpContext.Current.Request; var v = ValidateHelp.BeginEntity() .Require("UserName", "password", "Email", "Sex") .IsNullOrEmpty(form["UserName"], "用户名不能为空") .IsNullOrEmpty(form["password"], "密码不能为空", "password") .IsInt(form["Sex"], "无法识别性别", "Sex") .IsEmail(form["Email"], "邮箱地址错误", "Email"); if (v.IsValid) { Console.WriteLine("验证通过"); var m = v.Entity as User; } else { Console.WriteLine("验证未通过 信息:" + v.ErrorMsg); } /* 验证数据 自动填充Dictionary */ form["Email"] = "304885433"; v = ValidateHelp.BeginDic() .Require("UserName", "password", "Email", "Sex") .IsNullOrEmpty(form["UserName"], "用户名不能为空", "UserName") .IsNullOrEmpty(form["password"], "密码不能为空", "password") .IsInt(form["Sex"], "无法识别性别", "Sex") .IsEmail(form["Email"], "邮箱地址错误", "Email"); if (v.IsValid) { Console.WriteLine("验证通过"); } else { Console.WriteLine("验证未通过 信息:" + v.ErrorMsg); } foreach (var d in v.Data) { Console.WriteLine("{0}={1}", d.Key, d.Value); } } } public class User { public string UserName { get; set; } public string password { get; set; } public int Sex { get; set; } public string Email { get; set; } }}
定义了一个 ValidateHelp 对象,在初始化的时候,可以选择数据容器。
解释一下,为什么需要数据容器。我们在方法开始对参数做验证,验证通过后必然是要去使用它,使用时难免需要类型转换,所以干脆在验证的时候去指明一个数据容器,在数据验证通过后,将数据填充到指定的容器中即可,验证完毕后,直接取出来使用。
看看其中一个验证方法的定义。
public ValidateHelp IsNullOrEmpty(string s, string msg = null, string propertyName = null)
s:被验证的字符串
msg:验证失败的提示消息
propertyName:验证成功后保存的属性名称,当数据容器为Dictionary时为Key,当数据容器为Model时为属性名。
所以楼主的目的就是验证失败时,得到错误信息,验证成功,能自动把类型变换后的值保存。
反射的优化方案
由于上面说了,目前还在猜想阶段,在对实体进行赋值时,使用的是反射。
性能要求不高的场景下,用反射即可,要求高的时候,推荐一个朋友写的库,在整个软件生命周期,调用次数达到1w以上,性能十分强劲,大家可以看看,
说说Require
楼主在原来的项目中做查询,条件总数大概有十几个,其中4-5个是必需的,其他为null时可以忽略,加入Require方法,目的则是指明部分属性是不可以为null的。在上面的实例中,对于Sex字段验证时,该值为null,最后依然验证通过。
源码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Text.RegularExpressions;namespace MacRead{ public class ValidateHelp { #region 属性 //数据容器字典 public DictionaryData { get; private set; } //数据容器实体 public Object Entity { get; private set; } //错误信息 public string ErrorMsg { get; private set; } // true:验证通过 false:验证未通过 public bool IsValid { get; private set; } #endregion #region 字段 //数据容器实体类型 private Type EntityType; //必需的字段信息 private string[] RequiredProperty; #endregion #region 静态方法 得到对象实例 public static ValidateHelp Begin() { return new ValidateHelp(); } public static ValidateHelp BeginDic() { var v = new ValidateHelp(); v.Data = new Dictionary (); return v; } public static ValidateHelp BeginEntity () { var v = new ValidateHelp(); v.EntityType = typeof(T); v.Entity = Activator.CreateInstance(v.EntityType); return v; } #endregion #region 辅助方法 public object this[string key] { get { object o; if (Data.TryGetValue(key, out o)) return o; return null; } } /// /// 指明必需的属性 不允许为null /// /// ///public ValidateHelp Require(params string[] requiredProperty) { if (this.RequiredProperty != null) throw new Exception("Require can‘t be called multiple times "); this.RequiredProperty = requiredProperty; return this; } #endregion #region 私有方法 private ValidateHelp() { IsValid = true;//默认验证通过 } /// /// 数据验证不通过 /// /// 错误信息提示 ///private ValidateHelp SetInValid(string msg) { IsValid = false; ErrorMsg = msg; return this; } /// /// 数据验证通过 /// /// 数据 /// 属性名 ///private ValidateHelp SetValid(object value, string propertyName) { if (!IsValid) IsValid = true; if (propertyName != null && propertyName.Length > 0) { if (Data != null) { Data[propertyName] = value; } else if (Entity != null) { var p = Entity.GetType().GetProperty(propertyName); //为对象属性赋值 p.SetValue(Entity, value, null); } } return this; } /// /// 验证检查 返回True 终止验证,false 继续验证 /// /// 输入值 /// 属性值 ///private bool Interrupt(string s, string propertyName) { //验证无效,直接返回 if (!IsValid) return true; /*继续验证条件 * s非空 * 未指明属性名 * 未定义必需的属性 */ if (s != null || propertyName == null || propertyName.Length == 0 || RequiredProperty == null || RequiredProperty.Length == 0) return false; //必需属性数组中包含属性时, var i = Array.IndexOf(RequiredProperty, propertyName); if (i > -1) // 属性还被指定必需,则直接判定为无效,终止验证 { IsValid = false; ErrorMsg = propertyName + " is null"; } // 属性为null时 中断验证 return true; } #endregion #region 验证方法 public ValidateHelp IsNullOrEmpty(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; if (s == null || s.Length == 0) { return SetInValid(msg); } return SetValid(s, propertyName); } public ValidateHelp IsInt(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; int i; if (!int.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } public ValidateHelp IsDouble(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; double i; if (!double.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } public ValidateHelp IsLong(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; long i; if (!long.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } public ValidateHelp IsFloat(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; float i; if (!float.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } public ValidateHelp IsBool(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; bool i; if (!bool.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } public ValidateHelp IsDateTime(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; DateTime i; if (!DateTime.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } public ValidateHelp IsEnum (string s, string msg = null, string propertyName = null) where T : struct { T i; if (!Enum.TryParse(s, true, out i)) { return SetInValid(msg); } return SetValid(i, propertyName); } private static Regex regChar = new Regex(@"^[A-Za-z]+$"); public ValidateHelp IsChar(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; if (!regChar.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName); } private static Regex regNumber = new Regex(@"^\d+$"); public ValidateHelp IsNumber(string s, string msg = "") { if (Interrupt(s, string.Empty)) return this; if (!regNumber.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, string.Empty); } private static Regex regChinese = new Regex(@"^[\u4e00-\u9fa5]+$"); public ValidateHelp IsChinese(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; if (!regChinese.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName); } private static Regex regEmail = new Regex(@"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"); public ValidateHelp IsEmail(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; if (!regEmail.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName); } private static Regex regIP = new Regex(@"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); public ValidateHelp IsIP(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; if (!regIP.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName); } private static Regex regUrl = new Regex(@"^([a-zA-z]+://)?(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$"); public ValidateHelp IsUrl(string s, string msg = null, string propertyName = null) { if (Interrupt(s, propertyName)) return this; if (!regUrl.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName); } public ValidateHelp IsRegex(string s, string msg, string pattern) { if (Interrupt(s, string.Empty)) return this; if (!Regex.IsMatch(s, pattern)) { return SetInValid(msg); } return SetValid(s, string.Empty); } #endregion }}
结合Web应用
楼主做的项目一般是Web类型项目,有时候可能会觉得上面的调用方式有点烦躁。比如说:
IsNullOrEmpty(form["password"], "密码不能为空", "password")
这里写了2个password,第一个是从form中取值,第二个是想保存的Key或PropertyName。
在Web中,请求的Url参数在Request.QueryString中,Post的参数在Request.Form中,它们的类型都是NameValueCollection。那么把他们当初数据源,或许会更方便吧。
源码如下:
using System;using System.Collections.Generic;using System.Collections.Specialized;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Web;namespace MacRead{ public class RequestValidateHelp { #region 属性 //数据容器字典 public DictionaryData { get; private set; } //数据容器实体 public Object Entity { get; private set; } //错误信息 public string ErrorMsg { get; private set; } // true:验证通过 false:验证未通过 public bool IsValid { get; private set; } #endregion #region 字段 //数据容器实体类型 private Type EntityType; //必需的字段信息 private string[] RequiredProperty; //数据源 private NameValueCollection Container; #endregion #region 静态方法 得到对象实例 public static RequestValidateHelp Begin() { return new RequestValidateHelp(); } public static RequestValidateHelp BeginDic() { var v = new RequestValidateHelp(); v.Data = new Dictionary (); return v; } public static RequestValidateHelp BeginEntity () { var v = new RequestValidateHelp(); v.EntityType = typeof(T); v.Entity = Activator.CreateInstance(v.EntityType); return v; } #endregion #region 辅助方法 public object this[string key] { get { object o; if (Data.TryGetValue(key, out o)) return o; return null; } } /// /// 指明必需的属性 不允许为null /// /// ///public RequestValidateHelp Require(params string[] requiredProperty) { if (this.RequiredProperty != null) throw new Exception("Require can‘t be called multiple times "); this.RequiredProperty = requiredProperty; return this; } #endregion #region 私有方法 private RequestValidateHelp() { IsValid = true;//默认验证通过 var Request = System.Web.HttpContext.Current.Request; //构造请求上下文(无论是get还是post都允许携带Url参数,post时允许携带body Container = new NameValueCollection(Request.QueryString); if (Request.HttpMethod == "POST") { Container.Add(Request.Form); } } /// /// 数据验证不通过 /// /// 错误信息提示 ///private RequestValidateHelp SetInValid(string msg) { IsValid = false; ErrorMsg = msg; return this; } /// /// 数据验证通过 /// /// 数据 /// 属性名 ///private RequestValidateHelp SetValid(object value, string keyName) { if (!IsValid) IsValid = true; if (keyName != null && keyName.Length > 0) { if (Data != null) { Data[keyName] = value; } else if (Entity != null) { var p = Entity.GetType().GetProperty(keyName); //为对象属性赋值 p.SetValue(Entity, value, null); } } return this; } /// /// 验证检查 返回True 终止验证,false 继续验证 /// /// 输入值 /// 属性值 ///private bool Interrupt(string s, string propertyName) { //验证无效,直接返回 if (!IsValid) return true; /*继续验证条件 * s非空 * 未指明属性名 * 未定义必需的属性 */ if (s != null || propertyName == null || propertyName.Length == 0 || RequiredProperty == null || RequiredProperty.Length == 0) return false; //必需属性数组中包含属性时, var i = Array.IndexOf(RequiredProperty, propertyName); if (i > -1) // 属性还被指定必需,则直接判定为无效,终止验证 { IsValid = false; ErrorMsg = propertyName + " is null"; } // 属性为null时 中断验证 return true; } #endregion #region 验证方法 public RequestValidateHelp IsNullOrEmpty(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; if (s == null || s.Length == 0) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsInt(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; int i; if (!int.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsDouble(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; double i; if (!double.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsLong(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; long i; if (!long.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsFloat(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; float i; if (!float.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsBool(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; bool i; if (!bool.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsDateTime(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; DateTime i; if (!DateTime.TryParse(s, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsEnum (string keyName, string msg = null, string propertyName = null) where T : struct { var s = Container[keyName]; T i; if (!Enum.TryParse(s, true, out i)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } private static Regex regChar = new Regex(@"^[A-Za-z]+$"); public RequestValidateHelp IsChar(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; if (!regChar.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } private static Regex regNumber = new Regex(@"^\d+$"); public RequestValidateHelp IsNumber(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, string.Empty)) return this; if (!regNumber.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, string.Empty); } private static Regex regChinese = new Regex(@"^[\u4e00-\u9fa5]+$"); public RequestValidateHelp IsChinese(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; if (!regChinese.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } private static Regex regEmail = new Regex(@"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"); public RequestValidateHelp IsEmail(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; if (!regEmail.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } private static Regex regIP = new Regex(@"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); public RequestValidateHelp IsIP(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; if (!regIP.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } private static Regex regUrl = new Regex(@"^([a-zA-z]+://)?(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$"); public RequestValidateHelp IsUrl(string keyName, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, keyName)) return this; if (!regUrl.IsMatch(s)) { return SetInValid(msg); } return SetValid(s, propertyName ?? keyName); } public RequestValidateHelp IsRegex(string keyName, string pattern, string msg = null, string propertyName = null) { var s = Container[keyName]; if (Interrupt(s, string.Empty)) return this; if (!Regex.IsMatch(s, pattern)) { return SetInValid(msg); } return SetValid(s, string.Empty); } #endregion }}
在验证方法的参数中,依旧保留了2个Key一样的参数,看定义:
public RequestValidateHelp IsNullOrEmpty(string keyName, string msg = null, string propertyName = null)
propertyName 作为在保存验证成功后的值的键名或者属性名,如果为null,则默认使用keyName,即取值的Key。
为什么需要这样多次一举,看示例吧。
$("#btnLogin").on('click',function(){ var viewModel = { uid: $("#txtUserName").val(), pwd: $("#txtPassword").val() }; if (viewModel.uid == '') { alert('用户名不能为空'); return; } if (viewModel.pwd == '') { alert('密码不能为空'); return; } $.post('/Account/Login?tt=1', viewModel, function (response) { if (!response.Result) { alert(response.Message); return; } window.location.href = 'admin/index.html'; },'json'); });
js提交到后端的json数据是uid和pwd,但实际上后台定义的实体类似 UserName,Password等属性,呃,可能是不想过多的让别人了解后台的类定义吧。。。
写在最后
由于是近2天的思考,楼主希望能同大家交流下,时间有点仓促,代码中难免有疏漏或者错误的地方。
重在思想,代码的表现形式会有很多,更优秀的代码就是下一行。
下班了。