如何通过属性强制字符串的格式?

How to enforce the format of a string via attribute?

我有一个数据模型,它是一个字符串列表。这将由用户输入填充,但我需要确保他们提供的字符串的格式仅为“something:something”。

即它必须包含一个 冒号 后跟另一组字符(最小长度为 4)。当前的模型(在需要结肠附件之前)是沿着:

   [Required(ErrorMessage = "some error msg")]
   [MinLength(10)]
   public List<string> ListOfStrings { get; set; }

我的旧实现的唯一要求只是长度要求。我想知道实施这种新格式“string:string”的最佳方式是什么?我想拒绝不包含后跟 4 个或更多字符的冒号的字符串。

提前感谢您的任何建议。

由于您现在要验证数组的内容而不仅仅是数组本身,因此您最简单的解决方案是自定义属性:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class MyCustomValidatorAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        List<string> list = value as List<string>;
        if (list == null)
        {
            // not a string list, might want to handle this directly or just let it fall back to the other validators
            return base.IsValid(value, validationContext);
        }

        foreach (var item in array)
        {
            var hasError = // your string validation here

            if (hasError) {
                return new ValidationResult("Your validation error description here");
            }
        }

        return ValidationResult.Success;
    }
}

一旦您确定了该验证器的详细信息,用法将是:

[Required(ErrorMessage = "some error msg")]
[MinLength(10)]
[MyCustomValidator]
public List<string> ListOfStrings { get; set; }

虽然不知道您的期望是什么,但我能猜到的最适合您的实际字符串验证的可能是这样的:

// declare this only once before the loop
var isValidStringRegex = new Regex("^\w+:\w{4,}$");

var hasError = !isValidStringRegex.IsMatch(item);

此外,这并不是您问题的真正答案,但请查看 FluentValidation 是否适合您的项目。一旦你习惯了,编写测试就容易得多,也更容易思考。

你可以使用正则表达式,this is information about regex in msnd

在你的例子中,字符串 pattam 是 "\w+:(\w{4,})"

the regex online tool 或控制台中尝试

    public static void Main(string[] args)
    {
        System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"\w+:(\w{4,})");
        //Your code goes here
        Console.WriteLine(r.IsMatch("string:string").ToString());
    }

提出几个备选方案。您可以根据自己的需要和适用性参考和应用它。

解决方案 1:

应用IValidatableObject.Validate(ValidationContext)

您可以在 IValidatableObject.Validate(ValidationContext) Method 中编写您的验证逻辑。

先决条件:

  • class 必须使用 IValidatableObject 接口实现。
public class Input : IValidatableObject
{
    [Required(ErrorMessage = "some error msg")]
    [MinLength(10)]
    public List<string> ListOfStrings { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();
        if (this.ListOfStrings == null || !this.ListOfStrings.Any())
        {
            results.Add(new ValidationResult("ListOfStrings is required"));
            return results;
        }
            
        var regex = new Regex(@"^\w+:\w{4,}$");

        var hasUnmatched = this.ListOfStrings.Any(x => !regex.IsMatch(x));
        if (hasUnmatched)
        {
            results.Add(new ValidationResult("ListOfStrings contains invalid format string"));
            return results;
        }
            
        return results;
    }
}

注: 感谢@Callum Morrisson 的回答,解决方案 1 仅在不需要在不同 class.

中应用验证逻辑时才是首选

Sample data & result output


解决方案 2:

CustomValidation 属性与辅助方法

感谢@JohnWu 的建议,CustomValidationAttribute 使用 Helper 方法也是另一种选择。

public class Input
{
       [Required(ErrorMessage = "some error msg")]
       [MinLength(10)]
       [CustomValidation(
            typeof(InputValidationHelper),
            "ValidateListOfStringsRegex",
            ErrorMessage = "ListOfStrings contains invalid format string")]
       public List<string> ListOfStrings { get; set; } 
}

public class InputValidationHelper
{
    public static ValidationResult ValidateListOfStringsRegex(List<string> listOfStrings)
    {
        if (listOfStrings == null || !listOfStrings.Any())
        {
            return new ValidationResult("ListOfStrings is required");
        }
        
        var regex = new Regex(@"^\w+:\w{4,}$");

        var hasUnmatched = listOfStrings.Any(x => !regex.IsMatch(x));
        if (hasUnmatched)
        {
            return new ValidationResult(null);
        }
        
        return ValidationResult.Success;
    }
}

注: 将推荐解决方案 2,因为验证逻辑可以在不同的 classes 属性中重复使用 [不要重复自己 (D.R.Y)]。

看来 RegularExpressionAttribute 可以胜任:

public class CustomerMetaData
{
  
   // Allow up to 40 uppercase and lowercase 
   // characters. Use custom error.
   [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", 
        ErrorMessage = "Characters are not allowed.")]
   public object FirstName;

   // Allow up to 40 uppercase and lowercase 
   // characters. Use standard error.
   [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
   public object LastName;
}

另请注意,您也可以随时进行验证,例如作为控制器代码的一部分,没有任何属性。