【问题标题】:Is invalid user input a valid reason for throwing an exception?无效的用户输入是引发异常的正当理由吗?
【发布时间】:2012-01-27 03:18:23
【问题描述】:

根据.NET Framework General Reference: Error Raising and Handling Guidelines,在“正常”操作期间不应抛出异常。用户对 Web 表单的无效输入,比如用户输入了重复的名称,是否被认为是正常的? !!重要!!:我相信我们几乎都对此有意见,请附上对可靠来源的参考。

编辑:

更多背景知识:我正在质疑我正在阅读的一本书所提倡的模型验证方法。这本书建议您在提供要保存的无效数据时从存储库中抛出自定义异常。现在,在我看来,这违反了 MS 指导方针,因为您使用异常作为流控制......除非接收无效数据被认为是“正常”操作之外的。我只是想看看是否有来自可靠来源的进一步指导来解决这个问题。

另一个编辑:

好的,两年半后,我正在将此存储库移动到 WCF 服务,并在此方法中使用异常结果证明是个坏主意。哦,好吧。

【问题讨论】:

  • 重名可能是个例外。无效的电话号码是非严重错误的更好示例。
  • 根据您的编辑,我同意您正在阅读的书。你指的是哪本书?在我看来,应该保证包含我的实体的层是有效的——因此将无效数据传递给它们意味着堆栈更高层的某些东西是不正确的——无论是缺少/无效验证,需求的变化没有渗透到堆栈中,等
  • 重要的是要注意,在许多情况下,输入数据在某一时刻的有效性可能表明,但不能证明,输入在未来的任何时间都是有效的.必须为发现有效的数据在检查时间和使用时间之间变为无效的情况做好准备。预先检查有效性可能有助于改善用户体验,并且提供“Try”方法以避免抛出异常可能会有所帮助如果调用代码准备好处理。您对异常有什么问题?
  • 服务现在必须为异常指定 FaultContracts 并将异常作为 FaultExceptions 抛出。此外,必须更改所有客户端代码以捕获 FaultException。如果我在接口中指定了返回无效数据的方法,我可以继续使用现有接口而无需进一步修改。
  • @Paul:如果接口承诺返回有效数据,那么将规范更改为 void 该承诺将比引发异常更糟糕,不是吗?

标签: .net architecture exception-handling


【解决方案1】:

一般来说,无效或格式错误的输入不被视为“异常”,应使用异常以外的其他方式进行处理。但请注意,这是一个指导原则——在某些情况下,使用异常处理问题可能会产生更好的代码。

【讨论】:

    【解决方案2】:

    无效的用户输入是一种预期的情况。您希望它像有效输入一样经常发生。如果是这样,抛出异常可能太多了。

    另一方面,如果您出于某些原因更喜欢这种代码风格,您可以抛出自定义异常并在内部捕获它们。但是无效的用户输入不应该引发那种会完全停止您的应用程序的异常。

    【讨论】:

    • 假设数据的第一次检查是在 UI 中,当 UI 验证没有发生或变得过时并且与更深层次的实现(即数据库约束、域逻辑等)不一致时会发生什么.)?逻辑冲突是否值得例外?
    • 在输入正确之前,UI 不会将执行传递给 BL,BL 将再次检查并返回验证摘要而不引发异常,数据库完整性是最后一道防线,它应该已经引发一个例外,因为错误进入数据库这一事实表明存在严重错误。
    【解决方案3】:

    你是这样做的:

    在数据库方面,如果您尝试创建具有重复名称的新用户记录,则会抛出异常。这是一种特殊情况,您在数据库方面无能为力。您还提供检查用户名可用性的方法。

    在 UI 方面,您允许用户选择一个名称,然后检查其可用性。 UI 端负责与用户交互并告诉他们选择另一个名称。由于能够检查名称的有效性,UI 永远不应传入重复的名称...除非发生异常情况


    在这种情况下,我同意这本书。您的数据库是应用程序的最低级别,不应在其中编写太多业务逻辑(如果发生 A,则执行 B,除非 C,然后执行 D)。这并不意味着您不能在数据层中提供有用的方法来避免异常。

    【讨论】:

    • 这就是我们实施方式背后的原因。 “一般规则”是越接近验证发生的 UI,它就越轻。我们确实在多个级别上进行了验证,最终目标是防止发现更昂贵的不良数据(即 SqlException)。在我们的 DDD 方法中,我们严格执行对象的有效性,“异常”是数据未经过验证或验证规则与实现不一致。
    【解决方案4】:

    异常是异常的东西——这就是为什么它们被称为异常。作为一般规则,错误的用户输入也不例外,应该通过向所述用户发出某种类型的通知来优雅地处理。另外,不要忘记异常确实会影响代码的性能。

    http://blogs.msdn.com/kcwalina/archive/2005/03/16/396787.aspx http://msdn.microsoft.com/en-us/magazine/dd419661.aspx

    【讨论】:

    • 您的代码在 UI 驱动事件中的性能到底应该如何?担心异常的性能是浪费时间,除非您已将其确定为导致代码性能不佳的原因。
    • 不完全正确。例外被称为例外,因为有人,可能是他们的发明者,这样称呼他们。你的论点让我想起了“一只猫比没有猫多一条尾巴。没有猫有两条尾巴,因此一只猫必须有三条”。也就是说,您建议不要在这里使用异常处理可能仍然是正确的。
    【解决方案5】:

    通常不会,但我能想到我个人遇到的规则的一个例外。

    我们要求我们的域对象始终有效。如果尝试创建或传递错误数据,我们会从域对象中抛出异常。但是,在这种情况下,这是一种“特殊情况”。为什么?逻辑是不良数据永远不应进入域。在调用堆栈的某个地方,可以将无效数据输入到系统中——无论是通过计算错误、来自底层数据源的错误数据,还是来自用户输入。

    我们这样做的另一个辅助原因是域对象及其规则被封装在一个物理分离的程序集中。通过这样做,我们确保向调用者提供尽可能多的信息。由于正在发生的事情的影响,假设调用者将记录这一点,以便了解真正的问题 - 即未验证数据。

    因此,如果您要查看数据是否尚未经过验证,或者验证自身的规则与您的数据持久性方法/功能不一致,我认为抛出该方法是完全有效的。在所有其他情况下,我倾向于避免抛出无效输入。

    【讨论】:

      【解决方案6】:

      如果您根据某些标准明确验证用户输入,并计划根据验证结果采取行动,那么如果您不抛出异常,您会发现事情要容易得多。

      【讨论】:

        【解决方案7】:

        我听说过良好的 OO 实践不会为用户输入抛出异常。然而,这个逻辑也让我有点摸不着头脑。想想这里的历史。回到 C 编程中,我可能会编写一个函数,例如:

        int validateUserInfo(struct info)
        {
            //...
        }
        

        调用者会做这样的事情:

        int errorCode = validateUserInfo(info);
        if (errorCode != 0)
            handleError(errorcode);
        

        据我所知,编写异常处理是为了避免将错误条件作为返回值作为上述方法的返回值。然而,如果我不使用异常处理来验证用户信息,我是否不会返回一些关于错误数据的条件,这会导致我有一个 if 语句来检查无效数据以改变我的“成功”执行流程?

        现在看来我必须检查返回值并包含在 try/catch 中。 C++/C#/Java 类中的 validateUserInfo 方法可能会针对“异常错误”抛出异常,并将“错误数据”验证错误作为返回值或其他机制返回。这不是为了一些“OO规则”而使代码更复杂吗?

        当然,OO 纯粹主义者会带着一些替代方案回来,这样我就不必实际返回“坏数据”验证错误来试图取消这种情况,但事实是,有人会在某个地方编写代码来检查用于验证错误以及为异常编写 try/catch 块。

        如果异常处理速度很慢,那么编译器开发人员应该修复它以使其更快。这不应该是避免异常的原因。

        【讨论】:

          【解决方案8】:

          Steve McConnell 编写的 Code Complete 提供了一个非常好的防御性编程检查表。在例外主题下,他包括:

          • 您的项目是否有标准(这些标准胜过一切)
          • 有替代品吗?
          • 你能在方法内部适当地处理它吗?

          我的感觉是防御性编程应该始终由获取数据的方法来管理。因此,抛出异常是不合适的。

          当然,总会有减轻处罚的情况。

          【讨论】:

            【解决方案9】:

            我同意其他回答者的观点,即错误的输入是意料之中的,不应在例外情况下处理。但是,如果您发现恶意输入(sql 注入尝试或类似的东西),那么可能会出现异常。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-09-02
              • 2013-12-11
              • 1970-01-01
              • 2013-10-11
              • 2010-11-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多