Validation Web应用程序&x27;s表单验证-将域错误传播到客户端的设计?

Validation Web应用程序&x27;s表单验证-将域错误传播到客户端的设计?,validation,architecture,domain-driven-design,Validation,Architecture,Domain Driven Design,数据验证应在web应用程序中的以下位置进行: 客户端:浏览器。加快用户错误报告的速度 服务器端:控制器。检查用户输入是否在语法上有效(没有sql注入,例如,所有传入字段的有效格式、所有必填字段都已填写等) 服务器端:模型(域层)。检查用户输入是否在域范围内有效(无重复用户名,帐户余额不为负数等) 我目前是DDD迷,所以我的应用程序中有分离的UI层和域层 我还试图遵循规则,即域模型不应包含无效数据 那么,如何在应用程序中设计验证机制,以便域中发生的验证错误能够正确地传播到客户端?例如,当域模型

数据验证应在web应用程序中的以下位置进行:

  • 客户端:浏览器。加快用户错误报告的速度
  • 服务器端:控制器。检查用户输入是否在语法上有效(没有sql注入,例如,所有传入字段的有效格式、所有必填字段都已填写等)
  • 服务器端:模型(域层)。检查用户输入是否在域范围内有效(无重复用户名,帐户余额不为负数等)
我目前是DDD迷,所以我的应用程序中有分离的UI层和域层

我还试图遵循规则,即域模型不应包含无效数据

那么,如何在应用程序中设计验证机制,以便域中发生的验证错误能够正确地传播到客户端?例如,当域模型引发关于重复用户名的异常时,如何将该异常正确绑定到提交的表单

以下是一些激发这个问题的文章:


在我所知的web框架中,我没有见过这样的机制。我首先想到的是让域模型在异常数据中包含导致异常的字段名,然后在UI层中提供表单数据字段和模型数据字段之间的映射,以便在用户上下文中正确显示错误。这种方法有效吗?它看起来不稳。。。有一些更好的设计的例子吗?

尽管问题与之不完全相同,但我认为:

将验证逻辑封装到可重用类中。这些类通常称为规范、验证器或规则,是域的一部分

现在您可以在模型和服务层中使用这些规范。

如果您的UI使用与模型相同的技术,您也可以使用其中的规范(例如,在服务器上使用NodeJS时,您可以在JS中编写规范并在浏览器中使用它们)

编辑-聊天后的其他信息
  • 创建细粒度规范,以便在规范失败时能够显示适当的错误消息
  • 不要让业务规则或规范知道表单字段
  • 仅为业务规则创建规范,不为基本输入验证任务创建规范(例如检查null)

我想分享我们在一个DDD项目中使用的方法

  • 我们创建了一个基类,其中包含字段ErrorId& 错误消息
  • 每个DomainModel都派生自这个基类,因此有两个额外的字段ErrorId和ErrorMessage可从 基类

  • 每当发生异常时,我们都会处理异常(登录服务器,采取适当的步骤来补偿逻辑&从基于客户端位置的本地化资源文件中获取用户友好的消息以获取消息),然后以简单流的形式传播数据,而不会引发或引发异常

  • 在客户端检查ErrorMessage是否不为null,然后显示错误

这是我们从项目一开始就遵循的基本简单方法

如果是新项目,这是最不复杂和有效的方法,但如果您在大型旧项目中进行更改,这可能没有帮助,因为更改很大

对于每个字段级别的验证,请使用企业库中的验证应用程序块

它可以用作:

使用适当的属性装饰域模型属性,如:

public class AttributeCustomer 
{
    [NotNullValidator(MessageTemplate = "Customer must have valid no")]
    [StringLengthValidator(5, RangeBoundaryType.Inclusive, 
        5, RangeBoundaryType.Inclusive, 
        MessageTemplate = "Customer no must have {3} characters.")]
    [RegexValidator("[A-Z]{2}[0-9]{3}", 
    MessageTemplate = "Customer no must be 2 capital letters and 3 numbers.")]
    public string CustomerNo { get; set; }
}
创建验证程序实例,如下所示:

Validator<AttributeCustomer> cusValidator = 
            valFactory.CreateValidator<AttributeCustomer>();
检查验证结果,如下所示:

if (valResults.IsValid)
{
    MessageBox.Show("Customer information is valid");
}
else
{
    foreach (ValidationResult item in valResults)
    {
        // Put your validation detection logic
    }
}
代码示例取自 此链接将有助于理解验证应用程序块:


问题在于UI和域层中的数据集可能具有不同的结构。例如,用户模型有
用户名
电子邮件
密码
字段中创建。一切都是必需的<代码>密码是一个散列,在处创建的是一个日期。UI注册表只有
电子邮件
密码
重复密码
字段。两个密码都是字符串,必须相等。如何设计验证对象来统一验证两个数据集?后一种情况只是基本的输入验证。这与域逻辑无关。所以不要为此使用规范。一旦您认为输入格式正确,您可能希望根据业务规则对其进行验证-这是可重用规范可以提供帮助的地方。但是您必须根据数据库验证电子邮件以防止重复。这是服务/业务模型层的一部分。如何将
DuplicateEmailException
传播回UI层,并将其与表单的
email
字段关联?我同意,我考虑了一个非常简单的案例,但我希望你能理解。当然,我也可以用其他部分与模型连接的字段使示例表单饱和。这有点不同,因为您需要向服务器发出请求以验证表单字段。尽管如此,您可以将验证逻辑封装在规范中以使其可重用。唯一的区别可能是这个规范依赖于一个存储库,但我不明白为什么这会是一个问题,我指的是服务器验证,而不是ajax验证。你是说我应该为域对象创建特定于浏览器的代码<代码>规范>代码吗?你应该考虑用唯一性验证按钮来设计一个表单,并且它所做的只是执行一个查询。然后,当您提交表单时,您可以让数据库抛出唯一性异常,服务层可以捕获该异常并将其封装在一个漂亮的http响应中。@danfromisrael好的,该http响应应该重新提交表单,并带有字段“导致重复错误异常”,用红色标记,对吗?所以,我
if (valResults.IsValid)
{
    MessageBox.Show("Customer information is valid");
}
else
{
    foreach (ValidationResult item in valResults)
    {
        // Put your validation detection logic
    }
}