验证输入的正确方法

时间:2013-07-05 15:13:55

标签: c# validation

检查输入的最有效方法是什么?

  1. 如何防止嵌套和膨胀的if语句?
  2. 使用例外正确的做法吗?如果没有,我应该遵循什么样的方法?
  3. 糟糕的例子(我认为):

    public int doSthWithAge(int age)
    {
        if (age > 0)
        {
            if (age > 100)
            {
                throw new AgeIsTooHighException();
            }
        }
        else
        {
            throw new NoWayException();
        }
    ...
    }
    

    但有什么好方法?

    (如果您要提供任何特定于语言的信息,请执行此操作,就像我使用C#进行验证一样 - -syntax-wise <)>

2 个答案:

答案 0 :(得分:4)

一种常见的面向对象验证技术是将验证规则建模为第一类对象:

  1. 定义用于验证特定数据类型的公共接口
  2. 实现符合该接口的类或函数的集合
  3. 遍历此集合中的每个函数/对象并调用验证方法。返回值为true / false,或者可能是描述验证失败的对象(如果验证规则已通过,则为null)。在迭代规则集合时构建验证失败列表
  4. 以适当的方式向用户提供验证失败
  5. 你会看到很多图书馆都在使用这种技术。

    示例:

    // the entity you want to validate
    public class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }
    }
    
    public class ValidationFailure
    {
        public ValidationFailure(string description) { Description = description; }
        public string Description { get; set; }
        // perhaps add other properties here if desired
    }
    
    // note that this is generic and can be reused for any object to validate
    public interface IValidationRule<TEntity>
    {
        ValidationFailure Test(TEntity entity);
    }
    
    public class ValidatesMaxAge : IValidationRule<Person>
    {
        public ValidationFailure Test(Person entity)
        {
            if (entity.Age > 100) return new ValidationFailure("Age is too high.");
        }
    }
    
    public class ValidatesName : IValidationRule<Person>
    {
        public ValidationFailure Test(Person entity)
        {
            if (string.IsNullOrWhiteSpace(entity.Name))
                return new ValidationFailure("Name is required.");
        }
    }
    
    // to perform your validation
    var rules = new List<IValidationRule> { new ValidatesMaxAge(), new ValidatesName() };
    // test each validation rule and collect a list of failures
    var failures = rules.Select(rule => rule.Test(person))
        .Where(failure => failure != null);
    bool isValid = !failures.Any();
    

    此设计的优点:

    • 符合界面将提高代码模式的一致性
    • 每个验证规则的一个类或函数使您的规则保持原子,可读,自我记录,可重用
    • 遵循验证规则类的单一责任原则,并帮助简化需要执行验证的代码
    • 减少cyclomatic complexity(较少的嵌套if语句),因为它是一种更面向对象或功能更强的方法,而不是程序性的
    • 允许为个别验证规则类引入dependency injection,这在您访问数据库或服务类进行验证时非常有用

    编辑:@Mgetz提到输入验证与业务验证,这也是一个重要的考虑因素。上面描述的每个规则类的方法基于我每天使用的系统。我们更多地将它用于服务类中的业务逻辑验证(它是一个具有大量业务规则的复杂企业系统),并且该设计很好地满足了我们的目的。我上面写的具体规则非常简单,看起来更像是输入验证。对于输入验证,我建议采用更轻量级的方法,并在适当时将其与业务逻辑验证分开。

    这种设计的另一个实现是每个实体都有一个验证器类,并使用更轻量级的东西,例如lambdas用于单独的验证规则。例如,流行的Fluent Validation库使用它。这非常适合用户输入验证,因为它允许更少的代码进行简单的验证,并鼓励您将输入验证与业务逻辑验证分开:

    // Example using the FluentValidation library
    public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator()
        {
            RuleFor(p => p.Age).LessThan(100);
            RuleFor(p => p.Name).NotEmpty();
        }
    }
    

答案 1 :(得分:3)

验证效率通常不是问题。您应该关注两种类型的验证:

  1. 输入验证,例如将用户发送给恶意服务器并打算破解/破解我的应用程序的内容
  2. 业务验证,应该在您的业务逻辑中进行,并且应该是关于维护有效,一致的值。
  3. 跳过其中任何一种都是一种非常好的方法,最终导致被黑客攻击或严重破坏的应用程序。

    如果你正在使用ASP.net,那么有很多库(大多数来自微软)来做前者,Anti-XSS lib等。

    后者将在实体本身的共享业务逻辑中最有效地执行。 E.G您的用户实体不应允许年龄为-1。