5

想象一下,我有一个应用程序向用户请求一个名称、一个类别列表。当用户单击保存按钮时,应用程序会将名称和类别保存到数据库中。

我有一个从 UI 获取名称和类别的图层。该层检查是否有名称(长度> 0的字符串)。如果这是正确的,它会将名称传递给另一个层。注意:类别是一个单选按钮列表,其中始终选择一项。

在第二层,应用程序将根据类别选择合适的类来保存名称。

在最后一层,一个类会将此名称保存在数据库中。在这堂课上,我将检查名称是否为空。

我的问题是:检查方法的输入参数的正确位置在哪里?在每一层?也许,我将在其他开发中使用这些层。

我的例子正确吗?也许,我可以在数据库层留下验证并在 UI 层引发异常。

4

4 回答 4

3

一般来说,就有关验证最终持久化的输入的更大问题而言,最好:

  • 收到后尽快将输入参数转换为完全封装的业务对象。

  • 尽早验证并快速失败,然后等到你到达较低层——浪费资源,浪费时间,可能更复杂(更多的事情要回滚)。

  • 验证一次业务逻辑并将其作为对象实例化过程的一部分。(但请注意,视图逻辑和持久性逻辑的验证可能需要在其他层完成,并且与业务逻辑分开)

  • 使用 ORM(例如,Hibernate)对对象如何持久化进行建模,以便您可以纯粹在内存中的对象级别工作,并将持久性作为实现细节。将业务逻辑集中在对象层。

就方法验证本身而言,我同意 Oded 的观点——在每一层,它应该在方法输入时立即完成。同样,这是快速失败方法的一部分。但是,正如我上面提到的,这并不意味着您在每个方法(或每个层)都验证业务逻辑。我只是指验证输入的基本实践(通过断言或显式检查和抛出的异常)。

于 2011-02-27T15:36:05.447 回答
2

检查方法的输入参数的正确位置在哪里?在每一层?

是的,在每一层。这叫做纵深防御

在每一层上这样做有不同的原因:

  • UI/客户端代码:保持响应并避免数据无效时的往返
  • 业务层:确保遵守业务规则
  • 数据层:确保有效数据通过
于 2011-02-27T15:35:49.650 回答
2

我一直不同意关于一层的建议。这对我来说听起来太教条了。

所有的设计都代表着功能和成本之间的选择。您的验证选择也应该反映这一点。

我认为你的决定应该考虑层的共享和重用。

如果数据库由多个应用程序共享,则数据库不能依赖于每个应用程序正确验证。在这种情况下,数据库必须进行验证以保护自己。

在这个 SQL 注入攻击的时代,我认为在到达持久层之前进行绑定和验证是必须的。模式应该做它必须做的事情以确保引用和业务完整性(例如,列上的唯一约束和要求“非空”),但其他验证应该在访问数据库之前完成。

如果数据库完全由一个且只有一个应用程序拥有,并且有一个服务是进入数据的唯一网关,那么验证可以由该服务完成。可以免除在数据库层上重复验证的成本。

在 UI 和服务层之间也是如此。

客户端和服务层的双重验证很常见,因为服务在面向服务的体系结构中由许多客户端共享。今天,您的服务可能会被基于浏览器的 UI 使用;突然之间,旁边也出现了一群要求服务的移动应用程序。在这种情况下,服务绝对必须验证和绑定每个请求。

没有制造商将每个表面抛光到镜面质量。有时粗糙的铸造表面是允许的,因为研磨和抛光的好处可以忽略不计,而且成本太高。

与软件相同。

我不喜欢教条式的陈述。最好了解您的选择的含义。了解规则以及何时可以打破规则。

于 2011-02-27T15:48:28.537 回答
0

如果您要进行非常松散的耦合,而其他应用程序也将使用这些相同的层,那么我建议在每一步都进行输入检查。由于每个类/方法都不应该知道堆栈中在它们之前运行的其他类/方法的期望,因此每个类/方法都应该单独执行其要求。

  • 如果 UI 要求在单击按钮时文本框中有一个值,它应该相应地进行验证。
  • 如果业务逻辑要求名称从不为空/空,则不应允许在该属性中放置空/空值​​。
  • 如果数据层对该字段有限制,要求存在值,则应在尝试持久化数据之前检查值是否存在。
于 2011-02-27T15:39:18.043 回答