在学asp.net MVC 中 用到了System.ComponentModel.DataAnnotations; 以特性的形式来给属性加数据有效性限定,于是有了一个想法,希望能够写一个只依赖于Model的验证方式,在研究asp.net MVC 数据验证的过程中,发现输入值如果不能转为了对应的属性值,也就是类型不匹配,如:Model属性值为int 而输入值为"t74",此时转换失败,将会调用MVC 默认的错误信息语句,用户无法指定具体报错信息,当然我们同样也可以通过客户端验证来弥补这一缺点,同时这种情况应该可以通过自定义ModelBinder来解决,这里就不多说了,有其它大神的文章可供参详。
以下是本人代码,做一个引子
一个学生类,给它的属性加了数据验证
public class Student { public int ID { set; get; } [Required(ErrorMessage = "不能为空!")] public string name { get; set; } [Required(ErrorMessage = "不能为空!")] [Range(1, 150, ErrorMessage = "年龄在{1}到{2}之间")] public int age { get; set; } [Required(ErrorMessage = "不能为空!")] [StringLength(50, ErrorMessage = "最长不超过50个字符!")] public string UserId { get; set; } [Required(ErrorMessage = "不能为空!")] [StringLength(20, ErrorMessage = "最长不超过20个字符!")] public string UserPwd { get; set; } [Required(ErrorMessage = "不能为空!")] public DateTime CreateTime { get; set; } [Required(ErrorMessage = "详细不能为空!")] public StudentDetails StuDetails { get; set; } }
学生类的子类,学生详细信息
public class StudentDetails { public bool Sex { get; set; } public string Address { get; set; } public StudentDetails(bool sex, string address) { this.Sex = sex; this.Address = address; } }
以下是验证Model的方法,我们需规定输入值和属性字段的映射关系,下面的MapValidateInfo 类就是用来记录输入值和Model属性的对应
关系,以及出错后所提示的信息,以下代码同时验证值类型字段的类型匹配问题和数据有效性验证问题。
public class Validate { /// /// 验证Model数据有效性,参数:type 为Model的type,mapinfos为该Model的属性和输入值的映射关系,errorInfos用来存放出错信息 /// public static object ValidateInstance(Type type, IEnumerable mapinfos, List errorInfos) { object instance = Activator.CreateInstance(type); IEnumerable pis = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Cast(); foreach (PropertyInfo pi in pis) { Models.MapValidateInfo map = mapinfos.FirstOrDefault(p => p.PropertyName == pi.Name); if (null != map) { object retobj = null; if (pi.PropertyType.IsValueType)//验证值类型有效性 { if (!ValidatePropertyType(pi.PropertyType, map.Val, out retobj)) { errorInfos.Add(new Models.ErrorInfo(pi.Name, map.ErrorMessage)); continue; } pi.SetValue(instance, retobj, null); } else { pi.SetValue(instance, map.Val, null); } //验证属性的数据有效性 ValidateAttribute(instance, pi, errorInfos); } } if (errorInfos.Count > 0) return null; return instance; } /// /// 验证属性的Attribute限定,instance:Model实例,pi:要验证的属性,errorInfos:存放错误信息 /// private static void ValidateAttribute(object instance, PropertyInfo pi, List errorInfos) { if (null == instance) return; IEnumerable pdsv = pi.GetCustomAttributes(typeof(ValidationAttribute), false).Cast(); foreach (ValidationAttribute vs in pdsv) { if (!vs.IsValid(pi.GetValue(instance, null))) { errorInfos.Add(new ErrorInfo(pi.Name, vs.FormatErrorMessage(string.Empty))); break; } } } /// /// 验证值类型输入值是否可转换为属性对应值类型,type:Model中属性类型,val:属性输入值,retobj:转换后的值 /// 可转换为true,不可转换为false private static bool ValidatePropertyType(Type type, object val, out object retobj) { retobj = null; var TryParse = type.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new Type[] { typeof(string), type.MakeByRefType() }, new ParameterModifier[] { new ParameterModifier(2) }); var parameters = new object[] { val, Activator.CreateInstance(type) }; bool success = (bool)TryParse.Invoke(null, parameters); if (success) { retobj = parameters[1]; } return success; } }
用来存放各字段的错误信息
/// /// 错误信息 /// public class ErrorInfo { /// /// 错误属性键值,同Model属性名 /// public string ErrorKey { get; set; } /// /// 错误信息 /// public string ErrorMessage { get; set; } public ErrorInfo(string errorkey, string errormsg) { this.ErrorKey = errorkey; this.ErrorMessage = errormsg; } }
/// /// 类型验证映射 /// public class MapValidateInfo { readonly string defaultErrorMessage = "{0}类型输入不正确!"; /// /// 对应的Model输入值 /// public object Val { set; get; } /// /// Model属性名 /// public string PropertyName { set; get; } /// /// 错误提示信息 /// public string ErrorMessage { get { return String.Format(defaultErrorMessage, this.PropertyName); } } public MapValidateInfo(string properyname, object val) { this.PropertyName = properyname; this.Val = val; } public MapValidateInfo(string properyname, object val, string errormessage) { this.PropertyName = properyname; this.Val = val; defaultErrorMessage = errormessage; } }
以下是页面代码,模拟提交的数据,
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { List list = new List();//存放输入值到属性的映射 List errors = new List();//错误信息 list.Add(new Models.MapValidateInfo("age", "0020")); list.Add(new Models.MapValidateInfo("CreateTime", "2004-10-10")); list.Add(new Models.MapValidateInfo("name", "pengyi_205")); list.Add(new Models.MapValidateInfo("UserPwd", "pengyi_205")); list.Add(new Models.MapValidateInfo("UserId", "ewfewfwefwefewf")); list.Add(new Models.MapValidateInfo("StuDetails", new Models.StudentDetails(false,"www.csdn.com"))); object obj = Models.Validate.ValidateInstance(typeof(Models.Student), list, errors); if (errors.Count > 0) { //有错误; } Models.Student s = obj != null ? obj as Models.Student : null; } }
上以是全部代码 ,需要下载Demo 的朋友:下载Demo