c# - 验证输入的正确方法

标签 c# validation

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

  1. 如何防止嵌套和臃肿的 if 语句?
  2. 使用异常(exception)是正确的做法吗?如果不是,我应该遵循哪种方法?

不好的例子(我认为):

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

但是有哪些好的方法呢?

(如果您要提供任何特定于语言的信息,请像我在 C# 中进行验证一样进行 -syntax-wise-)

最佳答案

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

  1. 定义用于验证特定类型数据的通用接口(interface)
  2. 实现符合该接口(interface)的类或函数的集合
  3. 遍历此集合中的每个函数/对象并调用验证方法。返回值要么是 true/false,要么是一个描述验证失败的对象(如果验证规则通过则为 null)。在遍历规则集合时构建验证失败列表
  4. 以适当的方式向用户展示验证失败

您会看到许多库都在使用这种技术。

例子:

// 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();

这种设计的优点:

  • 符合接口(interface)将促进代码模式的一致性
  • 每个验证规则一个类或一个函数使您的规则保持原子性、可读性、 self 记录、可重用
  • 遵循验证规则类的单一职责原则,有助于简化需要执行验证的代码
  • 减少 cyclomatic complexity (更少的嵌套 if 语句),因为它是一种更面向对象或功能性的方法,而不是过程性的
  • 允许引入 dependency injection对于单个验证规则类,如果您访问数据库或服务类进行验证,这将很有用

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

此设计的另一种实现方式是为每个实体设置一个验证器类,并使用更轻量级的东西(例如 lambda)来实现单独的验证规则。这是流行的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();
    }
}

关于c# - 验证输入的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17492089/

相关文章:

javascript - JSON 中的浮点零 (0000000000000000E+00) 未在 jQuery (Windows)/JSONLint 中解析

c# - Linq to Sql 联合语句

c# - 在 VS2010 中添加引用

c# - 数轴的 C# 实现

c++ - 如何在 C++ 中使用 std::stoi() 验证 int 输入?

php - 如何验证 VIN 号码?

Java:如何在字符串中只允许使用整数和逗号?

c# - 如何使用正则表达式屏蔽字符串中的 SSN

c# - 在域中运行的单元测试中获取物理文件路径而不是域的相对路径

java - Java中获取注解元数据的方法