mvc 实体类 自定义验证特性

mvc 实体类验证的时候 如果有多个验证特性需要在属性上层叠很多个验证特性,显得属性特别臃肿并且也不够直观,极大地影响我使用它的兴趣,所以我想自定义一个验证特性,然后将所有需要验证的情形全部放在一个特性里,看上去更直观一点。

   [DataContract]
    public partial class Sys_Menu : BaseModel
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [DataMember]
        public int? MenuID { get; set; }

        [Validate(DisplayName = "菜单URL", MaxLength = 100)]
        [DataMember]
        public string URL { get; set; }

        [Validate(DisplayName = "菜单名", Required = true, MaxLength = 20)]
        [DataMember]
        public string Name { get; set; }

        [StringLength(100)]
        public string Description { get; set; }
        [DataMember]

        public int? ParentID { get; set; }

        public bool? IsActive { get; set; }

自定义的验证特性是不是看上去更清爽一点

实现

 public class ValidateAttribute : ValidationAttribute, IClientValidatable
    {
        private const string EmailPattern = @"^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$";
        private const string FixedPhonePattern = @"^(d{3,4}-)?d{6,8}$";
        private const string MobilePhonePattern = @"^1d{10}$";
        /// <summary>
        /// 是否必填项
        /// </summary>
        public bool Required { get; set; }

        /// <summary>
        /// 数据格式
        /// </summary>
        public FieldDataType DataType { get; set; }

        /// <summary>
        /// 展示名称
        /// </summary>
        public string DisplayName { get; set; }

        /// <summary>
        /// 正则表达式
        /// </summary>
        public string RegexPattern { get; set; }

        public int MaxLength { get; set; }

        public int MinLength { get; set; }

        /// <summary>
        /// 验证
        /// </summary>
        /// <param name="value"></param>
        /// <param name="regx"></param>
        /// <returns></returns>
        private bool IsMatch(string value, string regx)
        {
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            bool isMatch = Regex.IsMatch(value, regx);
            return isMatch;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (string.IsNullOrEmpty(DisplayName))
            {
                DisplayName = validationContext.MemberName;
            }
            //前后有空格
            if (value is string && !value.Equals(value.ToString().Trim().ToString()))
            {
                IPropertyAccessor propertyAccessor = Caches.PropertyAccessorCache.Get(validationContext.ObjectType.GetProperty(validationContext.MemberName));
                if (propertyAccessor != null)
                {
                    propertyAccessor.SetValue(validationContext.ObjectInstance, value.ToString().Trim().ToString());
                    value = value.ToString().Trim().ToString();
                }
            }
            ValidateResult result = Valid(value);
            if (!result.IsValid)
            {
                return new ValidationResult(result.ErrorMessage);
            }
            else
            {
                return ValidationResult.Success;
            }
        }

        /// <summary>
        /// 验证 如果不是必填项 只要不为空的才验证 待扩展
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public ValidateResult Valid(object value)
        {
            if (this.Required)
            {
                if (value == null)
                {
                    return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "不能为空" };
                }
                if (value is string)
                {
                    if (string.Empty.Equals(value.ToString()))
                    {
                        return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "不能为空" };
                    }
                }
                else if (value is ICollection)
                {
                    if (((ICollection)value).Count == 0)
                    {
                        return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "条目不能为空" };
                    }
                }

            }
            if (value != null)
            {
                switch (DataType)
                {
                    case FieldDataType.Email:
                        if (!string.IsNullOrEmpty(value.ToString()) && !IsMatch(value.ToString(), EmailPattern))
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "不满足邮箱格式" };
                        }
                        break;
                    case FieldDataType.FixedPhone:
                        if (!string.IsNullOrEmpty(value.ToString()) && !IsMatch(value.ToString(), FixedPhonePattern))
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "不满足固话格式" };
                        }
                        break;
                    case FieldDataType.MobilePhone:
                        if (!string.IsNullOrEmpty(value.ToString()) && !IsMatch(value.ToString(), MobilePhonePattern))
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "不满足手机格式" };
                        }
                        break;
                    case FieldDataType.Phone:
                        if (!string.IsNullOrEmpty(value.ToString()) && (!IsMatch(value.ToString(), MobilePhonePattern) || !IsMatch(value.ToString(), FixedPhonePattern)))
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "不满足电话格式" };
                        }
                        break;
                }
                if (!string.IsNullOrEmpty(RegexPattern))
                {
                    if (!string.IsNullOrEmpty(value.ToString()) && !IsMatch(value.ToString(), RegexPattern))
                    {
                        return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "格式不正确" };
                    }
                }
                if (MaxLength != 0)
                {
                    if (value is string)
                    {
                        if (!string.IsNullOrEmpty(value.ToString()) && value.ToString().Length > MaxLength)
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "超出数据最大长度" };
                        }
                    }
                    else if (value is ICollection)
                    {
                        if (((ICollection)value).Count > MaxLength)
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "超出最大条目" };
                        }
                    }
                }
                if (MinLength != 0)
                {
                    if (value is string)
                    {
                        if (!string.IsNullOrEmpty(value.ToString()) && value.ToString().Length < MinLength)
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "超出数据最小长度" };
                        }
                    }
                    else if (value is ICollection)
                    {
                        if (((ICollection)value).Count < MinLength)
                        {
                            return new ValidateResult { IsValid = false, ErrorMessage = DisplayName + "超出最小条目" };
                        }
                    }
                }
            }
            return new ValidateResult { IsValid = true, ErrorMessage = DisplayName + "验证通过" };
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            if (this.Required)
            {
                sb.Append("Required");
            }
            if (DataType != FieldDataType.None)
            {
                sb.Append(" DataType:" + DataType.ToString());
            }
            if (!string.IsNullOrEmpty(RegexPattern))
            {
                sb.Append(" RegexPattern:" + RegexPattern.ToString());
            }
            if (MaxLength != 0)
            {
                sb.Append(" MaxLength:" + MaxLength.ToString());
            }
            if (MinLength != 0)
            {
                sb.Append(" MinLength:" + MinLength.ToString());
            }
            return sb.ToString();
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            throw new NotImplementedException();
        }
    }

可以根据自己的想法去验证

最后说说为什么要重写一个ToString(),这个是给HelpPage 接口说明文档使用的 我们将这个特性扩展进去,接口文档里开发人员就可以看到验证的说明

  public class ModelDescriptionGenerator
    {
        // Modify this to support more data annotation attributes.
        private readonly IDictionary<Type, Func<object, string>> AnnotationTextGenerator = new Dictionary<Type, Func<object, string>>
        {
            { typeof(RequiredAttribute), a => "Required" },
            { typeof(RangeAttribute), a =>
                {
                    RangeAttribute range = (RangeAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum);
                }
            },
            { typeof(MaxLengthAttribute), a =>
                {
                    MaxLengthAttribute maxLength = (MaxLengthAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
                }
            },
            { typeof(MinLengthAttribute), a =>
                {
                    MinLengthAttribute minLength = (MinLengthAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
                }
            },
            { typeof(StringLengthAttribute), a =>
                {
                    StringLengthAttribute strLength = (StringLengthAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength);
                }
            },
            { typeof(DataTypeAttribute), a =>
                {
                    DataTypeAttribute dataType = (DataTypeAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString());
                }
            },
            { typeof(RegularExpressionAttribute), a =>
                {
                    RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern);
                }
            },
              { typeof(ValidateAttribute), a =>
                {
                    ValidateAttribute validateExpression = (ValidateAttribute)a;
                    return String.Format(CultureInfo.CurrentCulture, "Customer Validate: {0}", validateExpression.ToString());
                }
            },
        };

顺带说一下,假如我的实体层是独立的 也需要做一些配置

首先要自定义一个MultiXmlDocumentationProvider

 public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
    {
        /*********
    ** Properties
    *********/
        /// <summary>The internal documentation providers for specific files.</summary>
        private readonly XmlDocumentationProvider[] Providers;


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="paths">The physical paths to the XML documents.</param>
        public MultiXmlDocumentationProvider(params string[] paths)
        {
            this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray();
        }

        /// <summary>Gets the documentation for a subject.</summary>
        /// <param name="subject">The subject to document.</param>
        public string GetDocumentation(MemberInfo subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>
        /// <param name="subject">The subject to document.</param>
        public string GetDocumentation(Type subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>
        /// <param name="subject">The subject to document.</param>
        public string GetDocumentation(HttpControllerDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>
        /// <param name="subject">The subject to document.</param>
        public string GetDocumentation(HttpActionDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>
        /// <param name="subject">The subject to document.</param>
        public string GetDocumentation(HttpParameterDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>
        /// <param name="subject">The subject to document.</param>
        public string GetResponseDocumentation(HttpActionDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }


        /*********
        ** Private methods
        *********/
        /// <summary>Get the first valid result from the collection of XML documentation providers.</summary>
        /// <param name="expr">The method to invoke.</param>
        private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr)
        {
            return this.Providers
                .Select(expr)
                .FirstOrDefault(p => !String.IsNullOrWhiteSpace(p));
        }
    }
  public static class HelpPageConfig
    {
        [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
            MessageId = "Panasia.UserCenter.WebAPI.Areas.HelpPage.TextSample.#ctor(System.String)",
            Justification = "End users may choose to merge this string with existing localized resources.")]
        [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly",
            MessageId = "bsonspec",
            Justification = "Part of a URI.")]
        public static void Register(HttpConfiguration config)
        {
            // Uncomment the following to use the documentation from XML documentation file.
            //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
            config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/UserCenter.Model.xml"), HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
            

右键项目属性设置XML文件生成路径

..XXX.UserCenter.WebAPIApp_DataXXX.UserCenter.Model.xml

原文地址:https://www.cnblogs.com/njcxwz/p/7170499.html