你在哪里进行验证?模型、控制器还是视图?

13
在Web表单应用程序中,您在哪里放置用户输入验证?
  1. 视图:JavaScript客户端
  2. 控制器:服务器端语言(C#...)
  3. 模型:数据库(存储过程或依赖项)
我认为每个级别都需要验证:
  1. 用户输入合理的值
    • 日期是实际日期,数字是实际数字...
  2. 再次执行1中的所有检查以及对恶意攻击(如XSS或SQL注入)的检查
    • 在1中执行的检查主要是为了避免用户犯错误时产生服务器往返。
    • 由于它们在javascript中在客户端上运行,因此无法相信它们已被运行。 再次验证这些值将停止一些恶意攻击。
  3. 是否满足依赖关系(即用户是否向有效问题添加了评论)
    • 一个良好的界面使这些非常难以违反。 如果在此处捕获到某些内容,则出现了严重问题。
[灵感来自此答复]

*Where(在哪里),*Controller(控制器)-- 请审校。 - GEOCHET
需要更多的校对: "mianly" = "主要是" - cori
1
模型不仅是数据库,它也是您应用程序的业务逻辑。为了避免违反DRY原则,验证应该在模型层中完成。 - Ross
10个回答

8

我检查所有层级,但我想注意一下我使用的验证技巧。

我在数据库层面进行验证,在您的模型上设置适当的约束将提供自动数据完整性验证。

这是一种艺术,似乎大多数Web程序员已经失去了它。


2
是的,但插入失败并不容易找到其失败的原因 - Brent
1
当然,SQL错误在我的设计中会向上冒泡到业务层,并且业务层会意识到失败的原因。 - FlySwat
2
但是为什么要让它走到那一步呢?你本可以在此之前轻松地进行检查。你可以像解析错误消息一样轻松地验证数据。 - Brent
1
因为当您有10,000个并发用户时,唯一理智的地方就是数据模型。当您有大量并发事务时,许多本应在业务逻辑中被捕获的错误可能会悄悄溜走。 - FlySwat
1
在数据库中通常需要满足非常复杂的约束条件。任何好的数据库都被优化为快速高效地评估这些依赖关系。有时让数据库失败比请求数据库获取相关记录并在业务层进行检查更快。 - alumb
我猜这取决于你正在进行什么类型的验证。在数据库中遵守唯一键可能更容易实现,但还有很多事情我宁愿保留在业务层面上。不过你提出了很好的观点。 - Brent

5
在模型中进行验证,界面中的自动化程序可以从模型中获取提示并改善用户体验。
所谓的自动化程序是指用户界面中不应该有任何针对特定模型的验证代码。如果您有一组验证方法库,例如 RoR 的(其中包括像 validates_presence_of :username 这样的方法),控制器或视图应该能够读取这些方法并应用等效的 JavaScript(或其他方便的)方法。
这意味着您将不得不在 UI 中复制完整的验证库,或者至少提供一个映射,如果您使用预先存在的验证库。但是一旦完成了这个步骤,您就不必在模型之外编写任何验证逻辑。

2
你能解释一下“自动化例程”是什么意思吗? - alumb

4

验证可以在所有层面上进行。

从Web表单中验证输入(所有字符串,转换为正确的类型等)与从Web服务或XML文件等验证输入是不同的。每个都有自己的特殊情况。当然,您可以创建一个Validator帮助类,从而将Validation外部化,并允许其被视图共享。

然后,您还需要进行DAO层验证-模型中是否有足够的数据进行持久化(以满足非空约束等)。您甚至可以在数据库中设置检查约束(例如,状态是否为('N','A','S','D')等)。


问题不在于它可以在哪里完成,而是应该在哪里完成。 对我来说,“所有层”似乎有点过头了。 速度应该成为所有应用程序的一个因素。 - alumb
速度应该是一个因素。然而,一些小的检查所带来的巨大开销将远远被HTTP和网络开销所抵消。 - JeeBee

3
很有趣。很长一段时间以来,我在模型中执行所有验证,就在我认为的DAL(数据访问层)上方。我的模型通常是按表数据网关模式来设计的,其中DAL提供抽象和低级API。
在TDG内部,我会实现业务逻辑和验证,例如:
1. 用户名是否为空 2. 用户名是否超过30个字符 3. 如果记录不存在,则返回错误
随着应用程序复杂性的增加,我开始意识到很多验证可以在客户端使用JavaScript完成。因此,我将大部分验证逻辑重构为JS,并清理了我的模型。
然后我意识到服务器端验证(不是过滤/转义-我认为这是不同的)也应该在服务器端完成,只在客户端作为锦上添花。
所以验证逻辑又回来了,当我再次意识到,输入验证/断言和业务规则/逻辑之间可能存在明显的区别。
基本上,如果可以在应用程序的客户端(使用JS)完成,则认为这是输入验证...如果必须由模型完成(例如,该记录已经存在等),则认为是业务逻辑。令人困惑的是,它们都保护数据模型的完整性。
如果您不验证用户名的长度,那么有什么阻止别人创建一个单字符用户名呢?
我仍然没有完全决定在哪里放置这种逻辑,我认为这取决于您更倾向于什么:轻量级控制器、重型模型还是反之亦然...
在我的情况下,控制器往往更加应用程序中心化,而如果精心制作,则可以将模型在“其他”项目中重复使用,而不仅仅是在内部使用,因此我喜欢保持模型的轻量级和控制器的重型。
推动您朝某个方向发展的力量实际上是个人意见、要求、经验等...有趣的话题 :)

3

验证必须在控制器中完成 - 这是确保安全性和响应的唯一位置。

验证应该在视图中完成 - 这是接触点,可以提供最佳用户体验并节省服务器额外工作。

验证将在模型上进行 - 但仅限于某些核心级别的检查。数据库应始终反映适当的约束条件,但让其成为真正的验证是低效的,并且数据库无法通过简单的约束条件确定有效输入。


2
所有验证都至少应该发生一次,并且这应该在中间层进行,无论是在您的值对象(在DDD意义上,不要与DTO混淆)还是通过实体本身的业务对象。客户端验证可以发生以增强用户体验。我倾向于不进行客户端验证,因为我可以一次性暴露表单中所有错误的事情,但这只是我的个人偏好。数据库验证可以发生以确保数据完整性,以防您在中间层中出现逻辑错误或后端某些问题。

0

我只在视图和控制器中进行操作,数据库通过数据类型等强制执行一些操作,但我宁愿在没有捕获错误之前就不要让它走得太远。

你基本上已经回答了自己的问题,需要知道的重要事情是你永远不能信任视图,尽管这是向用户提供反馈的最简单途径,所以你需要在至少一个更高级别上进行净化处理。


0

0

由于大多数验证依赖于业务规则,因此我将验证作为第三方工具类在业务层上进行。还有其他类型的验证,例如用户输入,需要在控制器中进行,但您也可以将这些验证规则封装在第三方类中。实际上,这取决于要验证什么。

客户端验证只是用来构建轻量级输入验证的较小部分,但服务器端验证始终是必需的。您永远不能信任用户输入 ;)

.NET拥有很好的控件来构建验证,但业务层始终需要更好的方法来验证数据,而这些控件不足以完成此任务。


-1

在视图中进行简单的输入验证,在模型中进行完整的验证。原因?如果您更改了视图技术,并且验证在视图/控制器中,则必须为新视图重新编写验证。这可能会引入错误。将其放入模型中,这将被所有视图重用...

但是,正如我所说,为了速度和简便性,在视图中进行简单的验证。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接