使用 DataAnnotations(数据注解)实现通用模型数据校验

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 DataAnnotations(数据注解)实现通用模型数据校验相关的知识,希望对你有一定的参考价值。

.net 跨平台

参数校验的意义

在实际项目开发中,无论任何方式、任何规模的开发模式,项目中都离不开对接入数据模型参数的合法性校验,目前普片的开发模式基本是前后端分离,当用户在前端页面中输入一些表单数据时,点击提交按钮,触发请求目标服务器的一系列后续操作,在这中间的执行过程中(标准做法推荐)无论是前端代码部分,还是服务端代码部分都应该有针对用户输入数据的合法性校验,典型做法如下:

  • 前端部分:当用户在页面输入表单数据时,前端监听页面表单事件触发相应的数据合法性校验规则,当数据非法时,合理的提示用户数据错误,只有当所有表单数据都校验通过后,才继续提交数据给目标后端对应的接口;

  • 后端部分:当前端数据合法校验通过后,向目标服务器提交表单数据时,服务端接收到相应的提交数据,在入口源头出就应该触发相关的合法性校验规则,当数据都校验通过后,继续执行后续的相关业务逻辑处理,反之则响应相关非法数据的提示信息;

特别说明:在实际的项目中,无论前端部分还是服务端部分,参数的校验都是很有必要性的。无效的参数,可能会导致应用程序的异常和一些不可预知的错误行为。

常用的参数校验项

这里例举一些项目中比较常用的参数模型校验项,如下所示:

  • Name:姓名校验,比如需要是纯汉字的姓名;

  • Password:密码强度验证,比如要求用户输入必须包含大小写字母、数字和特殊符号的强密码;

  • QQ:QQ 号码验证,是否是有效合法的 QQ 号码;

  • China Postal Code:中国邮政编码;

  • IP Address:IPV4 或者 IPV6 地址验证;

  • Phone:手机号码或者座机号码合法性验证;

  • ID Card:身份证号码验证,比如:15 位和 18 位数身份证号码;

  • Email Address:邮箱地址的合法性校验;

  • String:字符串验证,比如字段是否不为 null、长度是否超限;

  • URL:验证属性是否具有 URL 格式;

  • Number:数值型参数校验,数值范围校验,比如非负数,非负整数,正整数等;

  • File:文件路径及扩展名校验;

对于参数校验,常见的方式有正则匹配校验,通过对目标参数编写合法的正则表达式,实现对参数合法性的校验。

.NET 中内置 DataAnnotations 提供的特性校验

上面我们介绍了一些常用的参数验证项,接下来我们来了解下在 .NET 中内置提供的 DataAnnotations 数据注解,该类提供了一些常用的验证参数特性。

官方解释:

  • 提供用于为 ASP.NET MVCASP.NET 数据控件定义元数据的特性类。

  • 该类位于 System.ComponentModel.DataAnnotations 命名空间。

关于 DataAnnotations 中的特性介绍

让我们可以通过这些特性对 API 请求中的参数进行验证,常用的特性一般有:

  • **[ValidateNever]**:指示应从验证中排除属性或参数。

  • **[CreditCard]**:验证属性是否具有信用卡格式。

  • **[Compare]**:验证模型中的两个属性是否匹配。

  • **[EmailAddress]**:验证属性是否具有电子邮件格式。

  • **[Phone]**:验证属性是否具有电话号码格式。

  • **[Range]**:验证属性值是否位于指定范围内。

  • **[RegularExpression]**:验证属性值是否与指定的正则表达式匹配。

  • **[Required]**:验证字段是否不为 null。

  • **[StringLength]**:验证字符串属性值是否不超过指定的长度限制。

  • **[Url]**:验证属性是否具有 URL 格式。

其中 RegularExpression 特性,基于正则表达式可以扩展实现很多常用的验证类型,下面的( 基于 DataAnnotations 的通用模型校验封装 )环节举例说明。

关于该类更多详细信息请查看,https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=net-7.0

基于 DataAnnotations 的通用模型校验封装

此处主要是使用了 Validator.TryValidateObject() 方法:

Validator.TryValidateObject(object instance, ValidationContext validationContext, ICollection<ValidationResult>? validationResults, bool validateAllProperties);

Validator 类提供如下校验方法:

Validator

基于 DataAnnotations 的特性校验助手实现步骤

  • 错误成员对象类 ErrorMember

namespace Jeff.Common.Validatetion;

/// <summary>
/// 错误成员对象
/// </summary>
public class ErrorMember

    /// <summary>
    /// 错误信息
    /// </summary>
    public string? ErrorMessage  get; set; 
    /// <summary>
    /// 错误成员名称
    /// </summary>
    public string? ErrorMemberName  get; set; 
  • 验证结果类 ValidResult

namespace Jeff.Common.Validatetion;

/// <summary>
/// 验证结果类
/// </summary>
public class ValidResult

    public ValidResult()
    
        ErrorMembers = new List<ErrorMember>();
    
    /// <summary>
    /// 错误成员列表
    /// </summary>
    public List<ErrorMember> ErrorMembers  get; set; 
    /// <summary>
    /// 验证结果
    /// </summary>
    public bool IsVaild  get; set; 
  • 定义操作正则表达式的公共类 RegexHelper(基于 RegularExpression 特性扩展)

using System;
using System.Net;
using System.Text.RegularExpressions;

namespace Jeff.Common.Validatetion;

/// <summary>
/// 操作正则表达式的公共类
/// Regex 用法参考:https://learn.microsoft.com/zh-cn/dotnet/api/system.text.regularexpressions.regex.-ctor?redirectedfrom=MSDN&view=net-7.0
/// </summary>   
public class RegexHelper

    #region 常用正则验证模式字符串
    public enum ValidateType
    
        Email,                 // 邮箱
        TelePhoneNumber,       // 固定电话(座机)
        MobilePhoneNumber,     // 移动电话
        Age,                   // 年龄(1-120 之间有效)
        Birthday,              // 出生日期
        Timespan,              // 时间戳
        IdentityCardNumber,    // 身份证
        IpV4,                  // IPv4 地址
        IpV6,                  // IPV6 地址
        Domain,                // 域名
        English,               // 英文字母
        Chinese,               // 汉字
        MacAddress,            // MAC 地址
        Url,                   // URL 
    

    private static readonly Dictionary<ValidateType, string> keyValuePairs = new Dictionary<ValidateType, string>
    
        ValidateType.Email, _Email ,
        ValidateType.TelePhoneNumber,_TelephoneNumber ,  
        ValidateType.MobilePhoneNumber,_MobilePhoneNumber , 
        ValidateType.Age,_Age , 
        ValidateType.Birthday,_Birthday , 
        ValidateType.Timespan,_Timespan , 
        ValidateType.IdentityCardNumber,_IdentityCardNumber , 
        ValidateType.IpV4,_IpV4 , 
        ValidateType.IpV6,_IpV6 , 
        ValidateType.Domain,_Domain , 
        ValidateType.English,_English , 
        ValidateType.Chinese,_Chinese , 
        ValidateType.MacAddress,_MacAddress , 
        ValidateType.Url,_Url , 
    ;

    public const string _Email = @"^(\\w)+(\\.\\w)*@(\\w)+((\\.\\w+)+)$"; // ^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$ , [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]2,4
    public const string _TelephoneNumber = @"(d+-)?(d4-?d7|d3-?d8|^d7,8)(-d+)?"; //座机号码(中国大陆)
    public const string _MobilePhoneNumber = @"^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d8$"; //移动电话
    public const string _Age = @"^(?:[1-9][0-9]?|1[01][0-9]|120)$"; // 年龄 1-120 之间有效
    public const string _Birthday = @"^((?:19[2-9]\\d1)|(?:20(?:(?:0[0-9])|(?:1[0-8]))))((?:0?[1-9])|(?:1[0-2]))((?:0?[1-9])|(?:[1-2][0-9])|30|31)$";
    public const string _Timespan = @"^15|16|17\\d8,11$"; // 目前时间戳是15开头,以后16、17等开头,长度 10 位是秒级时间戳的正则,13 位时间戳是到毫秒级的。
    public const string _IdentityCardNumber = @"^[1-9]\\d7((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d3$|^[1-9]\\d5[1-9]\\d3((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d3([0-9]|X)$";
    public const string _IpV4 = @"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d1,2)(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d1,2))3$";
    public const string _IpV6 = @"^\\s*((([0-9A-Fa-f]1,4:)7([0-9A-Fa-f]1,4|:))|(([0-9A-Fa-f]1,4:)6(:[0-9A-Fa-f]1,4|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3)|:))|(([0-9A-Fa-f]1,4:)5(((:[0-9A-Fa-f]1,4)1,2)|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3)|:))|(([0-9A-Fa-f]1,4:)4(((:[0-9A-Fa-f]1,4)1,3)|((:[0-9A-Fa-f]1,4)?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3))|:))|(([0-9A-Fa-f]1,4:)3(((:[0-9A-Fa-f]1,4)1,4)|((:[0-9A-Fa-f]1,4)0,2:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3))|:))|(([0-9A-Fa-f]1,4:)2(((:[0-9A-Fa-f]1,4)1,5)|((:[0-9A-Fa-f]1,4)0,3:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3))|:))|(([0-9A-Fa-f]1,4:)1(((:[0-9A-Fa-f]1,4)1,6)|((:[0-9A-Fa-f]1,4)0,4:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3))|:))|(:(((:[0-9A-Fa-f]1,4)1,7)|((:[0-9A-Fa-f]1,4)0,5:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d))3))|:)))(%.+)?\\s*$";
    public const string _Domain = @"^[a-zA-Z0-9][-a-zA-Z0-9]0,62(\\.[a-zA-Z0-9][-a-zA-Z0-9]0,62)+\\.?$";
    public const string _English = @"^[A-Za-z]+$";
    public const string _Chinese = @"^[\\u4e00-\\u9fa5]0,$";
    public const string _MacAddress = @"^([0-9A-F]2)(-[0-9A-F]2)5$";
    public const string _Url = @"^[a-zA-z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$";
    #endregion

    /// <summary>
    /// 获取验证模式字符串
    /// </summary>
    /// <param name="validateType"></param>
    /// <returns></returns>
    public static (bool hasPattern, string pattern) GetValidatePattern(ValidateType validateType) 
    
        bool hasPattern = keyValuePairs.TryGetValue(validateType, out string? pattern);
        return (hasPattern, pattern ?? string.Empty);
    

    #region 验证输入字符串是否与模式字符串匹配
    /// <summary>
    /// 验证输入字符串是否与模式字符串匹配
    /// </summary>
    /// <param name="input">输入的字符串</param>
    /// <param name="validateType">模式字符串类型</param>
    /// <param name="matchTimeout">超时间隔</param>
    /// <param name="options">筛选条件</param>
    /// <returns></returns>
    public static (bool isMatch, string info) IsMatch(string input, ValidateType validateType, TimeSpan matchTimeout, RegexOptions options = RegexOptions.None)
    
        var (hasPattern, pattern) = GetValidatePattern(validateType);
        if (hasPattern && !string.IsNullOrWhiteSpace(pattern))
        
            bool isMatch = IsMatch(input, pattern, matchTimeout, options);
            if (isMatch) return (true, "Format validation passed."); // 格式验证通过。
            else return (false, "Format validation failed."); // 格式验证未通过。
        

        return (false, "Unknown ValidatePattern."); // 未知验证模式
    

    /// <summary>
    /// 验证输入字符串是否与模式字符串匹配,匹配返回true
    /// </summary>
    /// <param name="input">输入字符串</param>
    /// <param name="pattern">模式字符串</param>    
    /// <returns></returns>
    public static bool IsMatch(string input, string pattern)
    
        return IsMatch(input, pattern, TimeSpan.Zero, RegexOptions.IgnoreCase);
    

    /// <summary>
    /// 验证输入字符串是否与模式字符串匹配,匹配返回true
    /// </summary>
    /// <param name="input">输入的字符串</param>
    /// <param name="pattern">模式字符串</param>
    /// <param name="matchTimeout">超时间隔</param>
    /// <param name="options">筛选条件</param>
    /// <returns></returns>
    public static bool IsMatch(string input, string pattern, TimeSpan matchTimeout, RegexOptions options = RegexOptions.None)
    
        return Regex.IsMatch(input, pattern, options, matchTimeout);
    
    #endregion
  • 定义验证结果统一模型格式类 ResponseInfo(此类通常也是通用的数据响应模型类)

namespace Jeff.Common.Model;

public sealed class ResponseInfo<T> where T : class

    /*
     Microsoft.AspNetCore.Http.StatusCodes
     System.Net.HttpStatusCode
     */

    /// <summary>
    /// 响应代码(自定义)
    /// </summary>
    public int Code  get; set; 

    /// <summary>
    /// 接口状态
    /// </summary>
    public bool Success  get; set; 

    #region 此处可以考虑多语言国际化设计(语言提示代号对照表)
    /// <summary>
    /// 语言对照码,参考:https://blog.csdn.net/shenenhua/article/details/79150053
    /// </summary>
    public string Lang  get; set;  = "zh-cn";

    /// <summary>
    /// 提示信息
    /// </summary>
    public string Message  get; set;  = string.Empty;
    #endregion

    /// <summary>
    /// 数据体
    /// </summary>
    public T? Data  get; set; 
  • 实现验证助手类 ValidatetionHelper,配合 System.ComponentModel.DataAnnotations 类使用

// 数据注解,https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=net-7.0
using System.ComponentModel.DataAnnotations;
using Jeff.Common.Model;

namespace Jeff.Common.Validatetion;

/// <summary>
/// 验证助手类
/// </summary>
public sealed class ValidatetionHelper

    /// <summary>
    /// DTO 模型校验
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static ValidResult IsValid(object value)
    
        var result = new ValidResult();
        try
        
            var validationContext = new ValidationContext(value);
            var results = new List<ValidationResult>();
            bool isValid = Validator.TryValidateObject(value, validationContext, results, true);
            result.IsVaild = isValid;

            if (!isValid)
            
                foreach (ValidationResult? item in results)
                
                    result.ErrorMembers.Add(new ErrorMember()
                    
                        ErrorMessage = item.ErrorMessage,
                        ErrorMemberName = item.MemberNames.FirstOrDefault()
                    );
                
            
        
        catch (ValidationException ex)
        
            result.IsVaild = false;
            result.ErrorMembers = new List<ErrorMember>
            
                new ErrorMember()
                
                    ErrorMessage = ex.Message,
                    ErrorMemberName = "Internal error"
                
            ;
        
        return result;
    

    /// <summary>
    /// DTO 模型校验统一响应信息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="model"></param>
    /// <returns></returns>
    public static ResponseInfo<ValidResult> GetValidInfo<T>(T model) where T : class
    
        var result = new ResponseInfo<ValidResult>();
        var validResult = IsValid(model);
        if (!validResult.IsVaild)
        
            result.Code = 420;
            result.Message = "DTO 模型参数值异常";
            result.Success = false;
            result.Data = validResult;
        
        else
        
            result.Code = 200;
            result.Success = true;
            result.Message = "DTO 模型参数值合法";
        
        return result;
    

如何使用 DataAnnotations 封装的特性校验助手?

  • 首先定义一个数据模型类(DTO),添加校验特性 ValidationAttribute

using System.ComponentModel.DataAnnotations;
using Jeff.Common.Validatetion;

namespace Jeff.Comm.Test;

public class Person

    [Display(Name = "姓名"), Required(ErrorMessage = "0必须填写")]
    public string Name  get; set; 

    [Display(Name = "邮箱")]
    [Required(ErrorMessage = "0必须填写")]
    [RegularExpression(RegexHelper._Email, ErrorMessage = "RegularExpression: 0格式非法")]
    [EmailAddress(ErrorMessage = "EmailAddress: 0格式非法")]
    public string Email  get; set; 

    [Display(Name = "Age年龄")]
    [Required(ErrorMessage = "0必须填写")]
    [Range(1, 120, ErrorMessage = "超出范围")]
    [RegularExpression(RegexHelper._Age, ErrorMessage = "0超出合理范围")]
    public int Age  get; set; 

    [Display(Name = "Birthday出生日期")]
    [Required(ErrorMessage = "0必须填写")]
    [RegularExpression(RegexHelper._Timespan, ErrorMessage = "0超出合理范围")]
    public TimeSpan Birthday  get; set; 

    [Display(Name = "Address住址")]
    [Required(ErrorMessage = "0必须填写")]
    [StringLength(200, MinimumLength = 10, ErrorMessage = "0输入长度不正确")]
    public string Address  get; set; 

    [Display(Name = "Mobile手机号码")]
    [Required(ErrorMessage = "0必须填写")]
    [RegularExpression(RegexHelper._MobilePhoneNumber, ErrorMessage = "0格式非法")]
    public string Mobile  get; set; 

    [Display(Name = "Salary薪水")]
    [Required(ErrorMessage = "0必须填写")]
    [Range(typeof(decimal), "1000.00", "3000.99")]
    public decimal Salary  get; set; 

    [Display(Name = "MyUrl连接")]
    [Required(ErrorMessage = "0必须填写")]
    [Url(ErrorMessage = "Url:0格式非法")]
    [RegularExpression(RegexHelper._Url, ErrorMessage = "RegularExpression:0格式非法")]
    public string MyUrl  get; set; 
  • 控制台调用通用校验助手验证方法 ValidatetionHelper.IsValid()ValidatetionHelper.GetValidInfo()

// 通用模型数据验证测试
static void ValidatetionTest() 

    var p = new Person
    
        Name = "",
        Age = -10,
        Email = "www.baidu.com",
        MobilePhoneNumber = "12345",
        Salary = 4000,
        MyUrl = "aaa"
    ;

    // 调用通用模型校验
    var result = ValidatetionHelper.IsValid(p);
    if (!result.IsVaild)
    
        foreach (ErrorMember errorMember in result.ErrorMembers)
        
            // 控制台打印字段验证信息
            Console.WriteLine($"errorMember.ErrorMemberName:errorMember.ErrorMessage");
        
    
    Console.WriteLine();

    // 调用通用模型校验,返回统一数据格式
    var validInfo = ValidatetionHelper.GetValidInfo(p);
    var options = new JsonSerializerOptions
    
        Encoder = javascriptEncoder.UnsafeRelaxedJsonEscaping, // 设置中文编码乱码
        WriteIndented = false
    ;
    string jsonStr = JsonSerializer.Serialize(validInfo, options);
    Console.WriteLine($"校验结果返回统一数据格式:jsonStr");

在控制台Program.Main 方法中调用 ValidatetionTest() 方法:

internal class Program

    static void Main(string[] args)
    
        Console.WriteLine("Hello, DataAnnotations!");
        
            #region 数据注解(DataAnnotations)模型验证
            ValidatetionTest(); 
            #endregion
        
        Console.ReadKey();
    

启动控制台,输出如下信息:

ValidatetionHelper.IsValid

如何实现自定义的验证特性?

当我们碰到这些参数需要验证的时候,而上面内置类提供的特性又不能满足需求时,此时我们可以实现自定义的验证特性来满足校验需求,按照微软给出的编码规则,我们只需继承 ValidationAttribute 类,并重写 IsValid() 方法即可。

自定义校验特性案例

比如实现一个密码强度的验证,实现步骤如下:

  • 定义密码强度规则,只包含英文字母、数字和特殊字符的组合,并且组合长度至少 8 位数

/// <summary>
/// 只包含英文字母、数字和特殊字符的组合
/// </summary>
/// <returns></returns>
public static bool IsCombinationOfEnglishNumberSymbol(string input, int? minLength = null, int? maxLength = null)

    var pattern = @"(?=.*\\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z\\d]).";
    if (minLength is null && maxLength is null)
        pattern = $@"^pattern+$";
    else if (minLength is not null && maxLength is null)
        pattern = $@"^patternminLength,$";
    else if (minLength is null && maxLength is not null)
        pattern = $@"^pattern1,maxLength$";
    else
        pattern = $@"^patternminLength,maxLength$";
    return Regex.IsMatch(input, pattern);
  • 实现自定义特性 EnglishNumberSymbolCombinationAttribute,继承自 ValidationAttribute

using System.ComponentModel.DataAnnotations;

namespace Jeff.Common.Validatetion.CustomAttributes;

/// <summary>
/// 是否是英文字母、数字和特殊字符的组合
/// </summary>
public class EnglishNumberSymbolCombinationAttribute : ValidationAttribute

    /// <summary>
    /// 默认的错误提示信息
    /// </summary>
    private const string error = "无效的英文字母、数字和特殊字符的组合";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    
        if (value is null) return new ValidationResult("参数值为 null");

        //if (value is null)
        //
        //    throw new ArgumentNullException(nameof(attribute));
        //

        // 验证参数逻辑 value 是需要验证的值,而 validationContext 中包含了验证相关的上下文信息,这里可自己封装一个验证格式的 FormatValidation 类
        if (FormatValidation.IsCombinationOfEnglishNumberSymbol(value as string, 8))
            //验证成功返回 success
            return ValidationResult.Success;
        //不成功 提示验证错误的信息
        else return new ValidationResult(ErrorMessage ?? error);
    

以上就实现了一个自定义规则的 自定义验证特性,使用方式很简单,可以把它附属在我们 请求的参数 上或者 DTO 里的属性,也可以是 Action 上的形参,如下所示:

public class CreateDTO

   [Required]
   public string StoreName  get; init; 
   [Required]
   // 附属在 DTO 里的属性
   [EnglishNumberSymbolCombination(ErrorMessage = "UserId 必须是英文字母、数字和特殊符号的组合")]
   public string UserId  get; init; 

...
// 附属在 Action 上的形参
[HttpGet]
public async ValueTask<ActionResult> Delete([EnglishNumberSymbolCombination]string userId, string storeName)

该自定义验证特性还可以结合 DataAnnotations 内置的 [Compare] 特性,可以实现账号注册的密码确认验证(输入密码和确认密码是否一致性)。关于更多自定义参数校验特性,感兴趣的小伙伴可参照上面案例的实现思路,自行扩展实现哟。

总结

对于模型参数的校验,在实际项目系统中是非常有必要性的(通常在数据源头提供验证),利用 .NET 内置的 DataAnnotations(数据注解)提供的特性校验,可以很方便的实现通用的模型校验助手,关于其他特性的用法,请自行参考微软官方文档,这里注意下RegularExpressionAttribute(指定 ASP.NET 动态数据中的数据字段值必须与指定的正则表达式匹配),该特性可以方便的接入正则匹配验证,当遇到复杂的参数校验时,可以快速方便的扩展自定义校验特性,从此告别传统编码中各种 if(xxx != yyyy) 判断的验证,让整体代码编写更佳简练干净。

以上是关于使用 DataAnnotations(数据注解)实现通用模型数据校验的主要内容,如果未能解决你的问题,请参考以下文章

EF CodeFirst数据注解特性详解

ASP.NET-表单验证-DataAnnotations

EF Code-First 学习之旅 DataAnnotations

Asp.net MVC 数据注解与验证

译第17节---数据注解-Column

ASP.Net MVC开发基础学习笔记:四校验AJAX与过滤器