【问题标题】:Should DTO and Entity both have input validationsDTO 和实体是否都应该有输入验证
【发布时间】:2013-12-04 04:52:00
【问题描述】:

我有一个 WCF 层,我的领域模型位于这个 WCF 层后面。我使用 Nhibernate 作为 ORM 工具,我所有的业务逻辑/数据访问等都将在这个 WCF 层之后。

我将 DTO 暴露给我的客户。我有以下问题

1) 我应该创建 DTO 吗?将实体直接暴露给 wcf 客户端是否有任何危害,因为我的实体也将具有业务逻辑方法,因此我将不得不使用我认为不好的 WCF 属性破坏我的实体对象?

2) 如果我公开 DTO,我是否应该验证 DTO 以及实体。如果我只验证 DTO,那么我不会为我的 Enitity 对象提供任何输入验证。这样可以吗?

3) 我是否应该考虑使用模式验证来验证应用程序服务层(WCF 层)中的 DTO?或者我应该使用文章 [博客] 中给出的 IValidator 方法:http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/,如 Jimmy Bogard 所示

有时拥有 DTO 对我来说似乎是多余的,但我可以使用它来收集来自一个或多个实体的详细信息。

我会将这项服务公开给各种客户端,因此我的 DTO 将派生自一些具有凭据详细信息的基本 dto,我将在我的实际 wcf 方法调用之前检查每个传入的请求(可能使用 IEndpointBehaviour 和 IParamInspector)


编辑

基于我现在有点同意保留 DTO 层的响应,这是一个示例,以便场景变得更加明确

假设我的 WCF 应用程序服务层中有 CreateCustomer 方法接受 CustomerDetailsDTO,它可能由 MVC 应用程序调用。有一些输入验证,例如

输入验证:

i) 名称长度应大于 2 但小于 50 ii) 年龄是强制性的,不能低于 18 岁 (不同的其他字段验证)等

业务验证:

然后可能会有一些业务规则来检查重复的客户
基于说电子邮件或其他一些我认为应该是我的域业务逻辑的一部分并且应该驻留在 CustomerEntity 类中的因素。

当我们从客户端获取 DTO 时,输入验证是否应该仅应用于服务接口层 或者它也应该应用于 CustomerEntity

【问题讨论】:

    标签: c# wcf validation nhibernate domain-driven-design


    【解决方案1】:

    我应该创建 DTO 吗?将实体直接暴露给 wcf 客户端有什么危害吗

    1. 您应该创建 DTO。当实体通过 DataContract 公开时,WCF 从客户端可能发送到服务器的任何垃圾中创建这些实体。即使您计划稍后验证状态,创建具有无效/不一致状态的实体也是不好的做法。

    如果我公开 DTO,我是否应该验证 DTO 以及实体。如果我只验证 DTO,那么我不会为我的 Enitity 对象提供任何输入验证。这样可以吗?

    1. 无论是否验证实体,都应该验证 DTO。如前所述,DTO 包含来自客户端的任何垃圾。服务层负责验证每个服务方法参数值,并仅使用正确的值调用底层域层。

    2. 如果您确定在访问实体之前完成所有数据验证,则可以从实体中删除验证。

    【讨论】:

      【解决方案2】:

      1) 我应该创建 DTO 吗?将实体直接暴露给 wcf 客户端是否有任何危害,因为我的实体也将具有业务逻辑方法,因此我将不得不使用我认为不好的 WCF 属性破坏我的实体对象?

      是的,SOA 需要数据合同。

      它们或多或少可以是正式的(CSV、JSON、XSD、WSDL、WADL 甚至 HTML 或 txt 文件),但如果您无法就此类合同达成一致,则不应采用任何“服务”技术或技术(或任何其他重要的 IPC)。

      远程处理是唯一试图避免这种要求的技术。这是一个了不起的想法,抽象地说,但具体来说它不起作用。

      2) 如果我公开 DTO,我是否应该验证 DTO 以及实体。如果我只验证 DTO,那么我不会为我的 Enitity 对象提供任何输入验证。这样可以吗?

      您应该验证“合同”,而不是业务规则。

      例如,WCF DTO 可能需要填充一些字段,我会在构造函数中使用 ArgumentNullException

      但您应该记住,DTO 用于传输数据。如果您有一个数字字段由于某些奇怪的原因必须作为字符串传输,您可以验证它,例如防止 DTO 的初始化。

      3) 我是否应该考虑使用模式验证来验证应用程序服务层(WCF 层)中的 DTO?或者我应该使用文章 [博客] 中给出的 IValidator 方法:http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/,如 Jimmy Bogard 所示

      如果您需要一个领域模型(这意味着您需要聘请专家来了解应用程序的目的),它必须是唯一负责业务规则的人 .因此,对于简单的验证,您不需要任何验证框架。

      您需要的是expressive exceptions,它可以轻松映射到正确定义的故障。

      编辑以回答新问题

      在 WCF 中,我经常在 DTO 构造函数中使用输入验证,使客户端无法发送“无效请求”。这有很多优点,例如客户端不能使用无效输入来配置 DOS 攻击。此外,如果您有大量客户端,这可以减少网络负载,并使用户体验更好一些,因为他不需要等待服务器响应就知道他忘记了电子邮件字段中的 @。

      但实际上超过 18 岁是业务规则,而不是输入规则。

      输入规则可以是:“年龄字段必须大于零”,因为不可能出现负年龄,而且零年龄听起来太像用户错误(它是 int32 默认值)。

      但是合同验证还不够

      如果年龄与您的域相关,您将有一个 Age 结构,包装一个 UInt32(因此之前的 input 规则)。为什么要包装UInt32?例如,因为在您的域模型中,您知道两个用户年龄的总和没有意义。

      是的,您最多检查该数字 3 次(一次在客户端,两次在服务器上),但这是正确的做法,在这里。 DTO 可以独立于域模型发展,域模型不会冒意外行为的风险(或者您根本不需要域模型)。

      要了解业务规则,请考虑一个跟踪某种特殊治疗的医疗记录应用程序:command void Prescribe(Age patientAge, AntibioticPrescription prescription) 可以检查 patientAge 参数是否大于先前处方的年龄。这是业务规则。另一个业务规则应检查当前处方与之前处方之间的危险交互。

      如果是这样,此命令应该记录并抛出 3 个异常:

      • ArgumentNullException,当处方为空时(假设它是引用类型)
      • InconsistentAge,当提供的patientAge低于最后一个时
      • MortalPrescription,当这样的处方可以杀死病人时。

      这样的异常表示preconditions,它们是业务规则(除了参数null,当程序员引入某种错误时,它会尽快失败)。

      【讨论】:

      • 嗨 Giacomo,您能否详细说明“您应该验证“合同”,而不是业务规则。”你的意思是输入字段验证不是实体的一部分,应该在 DTO(数据契约)中注意?
      • 是的,我认为他的意思是您需要验证“数据合同”(他前面提到的)。因此,业务规则的验证将是实体的关注点,而使用有效的数据结构或有效数据应该是应用于 DTO 的关注点。无论如何,在持久化/更改任何数据之前,你都会得到两者,因为你不持久化 DTO,而是实体。 输入数据->DTO->实体->持久性;如果我理解正确的话。
      【解决方案3】:

      拥有 DTO 总是一件好事,可以将我们的域对象隐藏在任何 UI 或外部处理中。这还允许您制作稍微不同的对象结构,因为您的域模型可能与您的数据库图非常紧密,并且不一定总是反映对象的逻辑含义。

      意思是,不,您不应该直接通过服务公开实体。

      为了将实体转换为 DTO,我在大多数情况下都使用 Automapper,这非常方便。

      关于验证,例如,您可以在实体上使用数据注释来验证它们。

      但是,如果您的 DTO 反映了无法单独使用注释进行验证的不同类型或更复杂的业务逻辑,您可以使用类似 fluent validation 的东西,它非常灵活且易于设置。

      我目前在一个项目中使用它们。 例如,对于必填字段、字段长度甚至电子邮件或电话号码验证等标准验证,您可以使用带有正则表达式等的简单数据注释。

      对于复杂的事情,例如字段 A 是否为空字段 B 不能为空等...,您可以使用流利的验证...这真的取决于您是否在实体或 DTO 级别上执行此操作。如果您没有任何仅由 DTO 反映的自定义逻辑,可能是视图模型(就 MVC 而言),您可以在实体级别执行所有这些操作。

      这还取决于您的应用程序是否是唯一使用实体的应用程序。如果您打算将您的实体和数据访问层作为 api 公开给其他开发人员,则大部分验证都应在该级别完成。

      【讨论】:

      • 嗨 Micha,在实体上使用注释是个好主意,因为实体可以根据其在不同场景中的使用情况进行不同的验证集,而在其上应用注释会将其绑定到特定的验证。 (+1) 用于流畅的验证。
      • 这基本上就是我的意思,如果您放置注释(或在一般情况下验证实体),它应该是常见的验证,它不以任何方式特定于某些模型或业务逻辑。可以对模型/dto/whatever 进行任何特定的验证...
      猜你喜欢
      • 1970-01-01
      • 2011-07-11
      • 2022-01-19
      • 1970-01-01
      • 2020-09-25
      • 1970-01-01
      • 2011-01-22
      • 1970-01-01
      • 2019-06-07
      相关资源
      最近更新 更多