【问题标题】:MVC Question: Should I put form validation rules in the controller or model?MVC 问题:我应该将表单验证规则放在控制器还是模型中?
【发布时间】:2011-04-13 14:49:42
【问题描述】:

一方面,表单验证可以被视为应用程序逻辑的一部分,因此属于模型。

另一方面,它直接处理来自视图的输入并处理显示错误等。从这个角度来看,将其放入控制器中更有意义。

从 MVC 的角度来看,哪一种方法是正确的?

P.S 我的表单验证实际上只包括编写一个字段列表及其规则,并将其传递给表单验证库,该库返回 true/false 是否通过验证。

例子:

$this->load->library('form_validation');
$this->form_validation->set_rules('name', 'Name', 'required');
$this->form_validation->set_rules('email', 'Email', 'required|valid_email');
//........
if ($this->form_validation->validate())
    // Process data
else
    $this->register_form(); //A controller action that will show a view with errors

应该将其放入控制器或模型中吗?

【问题讨论】:

  • 我想那些说应该在 Controller 中完成验证的人来自 CakePHP-like-world,但我来自 Yii-like-world :) Validation in CakePHP VS Validation in Yii
  • @Mad 如果您以管理员身份编辑并希望省略所有字段,则控制器的工作是检查这一点,而不是调用验证方法。只需不进行验证就可以解决这种情况。
  • @Mad 我可以在模型本身中轻松检查用户是否以管理员身份登录,因为我的身份验证库对控制器和模型都可用。这可能是正确的做法。

标签: php codeigniter


【解决方案1】:

理想情况下,您需要 3 层验证:

  1. 视图: 客户端(javascript、html5 验证等)。这会在数据到达控制器之前捕获明显的错误和遗漏,从而浪费用户的时间并在出现错误时调用不必要的页面加载。
  2. Controller:这是您的 Form 验证层。控制器通常旨在直接处理输入,并将其发送到模型。表单中的每个字段在数据库中都有一个直接相关的列是非常罕见的,您通常需要在将数据传递给模型之前以某种方式更改数据。仅仅因为您有一个需要验证的字段,称为“确认电子邮件”,并不意味着您的模型将处理“确认电子邮件”值。有时,这将是最后的验证步骤。
  3. 模型:这是您验证的最后一道防线,并且可能是您在向模型发送数据直接来自表单帖子的情况下唯一的验证。很多时候,您需要通过控制器调用将数据发送到数据库,或者使用非用户输入的数据。我们不想看到数据库错误,我们想看到应用程序本身抛出的错误。模型通常不应该直接处理 $_POST 数据或用户输入,它们应该从控制器接收数据。您不想在这里处理无用的数据,例如电子邮件确认。

【讨论】:

  • 就是这样!控制器只是一个验证层!但是控制器不包含验证规则,它不知道如何验证用户提供的数据!控制器只知道请求了哪个动作,而动作创建了一个知道如何验证数据的体面模型。如果数据验证(按模型)失败,控制器只会呈现带有错误的适当视图。这正是我要说的。因此,数据验证完全是模型问题。我认为每个人都混淆了验证层(应该在什么时候验证数据)和验证位置(应该由谁/在哪里验证数据)。
  • @Nemoden 请看my example
  • 这是一个典型的答案! @ClickUpvote 我看不出这不是正确/最佳答案的原因
【解决方案2】:

我想说,在大多数情况下,表单验证代码应该在控制器(而不是模型)中。

Madmartigan 在“表单验证!== 数据验证。并非所有表单都与模型交互”上方的评论中说得最好。

Web 表单在逻辑上是 MVC 的视图/控制器部分的一部分,因为用户在视图中与它们进行交互。

【讨论】:

  • +1 - 我同意。在我看来,如果数据属于模型的想法在这里并不适用。
  • 什么样的表单不与Model交互?在我看来,您可以为所有类型的表单创建模型。数据确实属于模型。 Wiki:“模型不一定只是一个数据库;MVC 中的“模型”既是数据,也是应用程序中操作数据所需的业务/域逻辑”。在 Controller 中验证数据会使您的模型不完整。模型包含知道如何处理数据的业务逻辑。如果您在处理之前不验证您的数据(您忘记在 Controller 中进行验证),您就会遇到麻烦。就我而言,我永远不会忘记验证我的数据
  • 我同意 Nemoden 的观点,这是模型而不是控制器的责任。我认为你应该多读一点 MVC。
  • CodeIgniter 上下文中的关键字是 $this->FORM_validation,它指的是 html 表单,它是视图的一部分。正如您所说,“它直接处理来自视图的输入并处理显示错误等。从这个角度来看,将其放入控制器中更有意义。”如果将其放入模型中,则将模型代码耦合到需要 form_validation 对象,该对象是视图的一部分。如果您不打算在其他任何地方重用您的模型,您可以方便地执行此操作。但从纯粹主义者的角度来看,这是一种不好的做法。
  • @JohnWright 在 CodeIgniter 中,“form_validation”是一个“库”,控制器和模型都可以使用。此外,将它放入模型实际上更有意义,因为如果您有 2 个具有相同字段的表单:注册和“我的帐户”,但使用不同的控制器,两个控制器都可以调用 $this->user_model->validate () 查看表单是否经过验证。
【解决方案3】:

验证是模型的问题。只有模型知道您的数据应该是什么样子。你在模型中描述你的数据字段,所以你应该在同一个地方描述这些字段的验证规则。

这对我来说似乎很明显,但我很乐意听取对手的意见。

【讨论】:

  • 他应该将数据库验证放在模型中(假设它是一个db模型),并将http数据验证放在控制器中。例如,Xss 过滤与模型无关。它与输入中的控制器和输出中的视图有关。
  • 我们这里不谈数据过滤,只谈验证。为什么你不能在模型中执行过滤?您已从 Web 表单加载数据,将其加载到模型中,对数据进行模型验证,对其进行过滤,并将其保存在您想要的任何位置。过滤XSS只是数据转换,数据属于Model。也许我需要一些例子? ... 模型与他们保存数据的地方无关。如果您有将数据存储在数据库或文件中的模型,或者它不会将数据保存在任何地方,而只是通过电子邮件发送此数据(电子邮件发送表单)......
  • 我和 Nemoden 在一起。在我的例子中,XSS 过滤是由框架在后台处理的。我认为那些认为验证属于控制器的人应该更多地阅读 MVC(我犯了同样的错误)。
  • @Madmartigan,“在这种情况下,我们是否应该定义输入在模型中是否有效?我不这么认为”。你不这么认为,但和我想的一模一样。你为什么不这样做?应该有充分的理由吧?
  • @Madmartigan 问题是,您认为模型仅与数据库中的内容相关。这不是真的,该模型的工作是处理包括信用卡处理和验证在内的所有业务逻辑。您应该有一个 Booking 模型来验证表单并存储预订,以及一个 Order 模型来处理/验证 cc。控制器首先通过预订模型验证表单,然后通过 Order 模型验证 +charges cc,然后通过预订模型存储订单。如果有任何错误,它会将它们传递给查看。
【解决方案4】:

似乎每个人总是对这个问题说模范,这有其优点(与相反的情况相比),但我认为这个问题的答案更微妙。 应在模型上执行数据本身的验证

但还有其他类型的验证,例如提交的表单是否包含意外字段(显然是出于安全目的)或者用户是否有权进行请求的操作。通过将这些类型的验证放入模型中,它将模型(数据的抽象)整合为完全分离的事物,例如用户系统的工作方式或出于安全目的如何评估表单提交。

您可以想象更改其中一个类或类系统,然后因为您还必须更改所有模型而变得一团糟。而控制器是客户端输入和数据之间的中介:在该角色中,它们是上述示例的适当验证者,可能还有许多其他示例。

【讨论】:

  • 我觉得这叫数据/表单敏感。
【解决方案5】:

考虑到其他答案(和一些研究),如果您必须使用非空字段、电子邮件验证等规则验证数据,控制器不应该让这些数据通过自身,但如果您有规则像“只有信誉大于 150 的用户才能对答案投反对票”,你应该在模型层这样做。

如果您想要进行业务规则验证,我建议您使用像 Business Object Pattern 这样的对象,这样,当您想要“投票否决答案”时,您可以在软件的任何部分保留您的业务逻辑和集中。

【讨论】:

    【解决方案6】:

    这是一个有趣的理论讨论,但如果我们关注问题是在 Codeigniter(CI) 的背景下提出的:

    在 CI 中,您可以像这样指定自定义验证规则:

    $this->form_validation->set_rules('email', 'Email', 'required|callback_my_validation');
    

    在这种情况下,您必须定义一个名为“my_validation”的公共函数,该函数必须返回 true 或 false,并且框架会将错误(如果返回 false)添加到一堆错误。

    所以...如果你把这段代码放在控制器中,你会无意中公开一个公共 url,这意味着它可能会调用类似“http://yoursite.com/my_validation”的东西(我不认为你打算这样做)。 保护此 url 的唯一方法是进入“routes.php”文件并阻止对该 url 的访问。这似乎不切实际,似乎为我们指明了 CI 开发人员希望我们在模型中处理验证的方向。

    【讨论】:

    • 其实,如果你只是将你的验证函数设为私有,那么它就不能被url调用。即private function my_validation()。它不需要是公共功能。此外,它不能是模型上的函数,必须在控制器上定义才能使回调起作用。
    • 真的吗?谢谢,起初我在控制器上进行此验证,但当我将其设置为私有时,验证似乎停止工作。您提出的第二点似乎与我的第一个答案相反,我们是否应该得出结论认为 CI 开发人员打算在控制器上进行此验证? (如果是的话,上面的大部分观点都与此相矛盾)
    【解决方案7】:

    模型应该验证自己的数据。

    假设您有一个联系人模型,它只需要名字和电话号码。它应该验证名字和电话号码是否已填写。

    但是,如果此 Contact 模型是 Quote 的一部分,您可能还需要全名和电子邮件地址。

    在这种情况下,您可以扩展 Contact 模型(成为 QuoteContact 模型)并添加更多验证,或者您可以对 Quote 模型进行额外验证。

    您应该编写模型以便在其他应用程序中可重用(即使它们永远不会),因此它们应该独立于控制器。如果验证在控制器中,那么如果您切换到命令行版本,则会丢失这些验证。

    【讨论】:

      【解决方案8】:

      如果您使用 codeigniter 在服务器端验证表单,那么它会在控制器中验证

      您需要像这样使用自动加载来包含 form_validation 库

      $autoload['libraries'] = array("form_validation") 
      

      或者直接在Controller中加载

      $this->load->library('form_validation');
      

      然后为每个表单字段设置验证规则

      $this->form_validation->set_rules('username', 'User Name', 'required');
      $this->form_validation->set_rules('useremail', 'User Email', 'required|valid_email');
      

      如果在验证表单字段后发现任何错误,则它会在验证函数中捕获

      if ($this->form_validation->validate()) {
          //return back to form
      } else {
          //successful validate all field 
      }
      

      【讨论】:

        【解决方案9】:

        其他答案中没有涵盖另一个角度。这取决于您所说的 Controller / View 是什么!如果是 Javascript 在用户输入时检查验证,出于安全原因,您也应该在后端进行验证(这可能再次在后端的 控制器 中或模型,因为任何人都可以在没有浏览器的情况下通过 Ajax 推送数据。

        出于性能原因,您还应该在前端控制器/视图中进行验证,因为您不希望每次用户选择无效的出生日期或其他东西时都访问数据库.

        因此,除了 M、V、 和/或 C 中验证的理论基础之外,您还必须考虑前端与后端的实用性,而不考虑 MVC。

        我个人的建议是不要将自己限制在一个级别的验证中。放置不当的验证(如其他答案中提到的确认密码示例)可能会对架构产生严重影响。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-05-21
          • 2017-11-02
          • 1970-01-01
          • 1970-01-01
          • 2012-06-02
          • 2013-10-12
          • 1970-01-01
          • 2011-01-16
          相关资源
          最近更新 更多