我很困惑调用者或被调用者是否有责任检查数据的合法性。
callee 是否应检查传入的参数是否不应为null
并满足其他一些要求,以便 callee 方法能够正常且成功地执行,并抓住任何可能的例外情况?或者来电者有责任这样做吗?
答案 0 :(得分:74)
消费者方(客户端)和提供者方(API)验证。
客户应该这样做,因为这意味着更好的体验。例如,为什么网络往返只是被告知你有一个坏文本字段?
提供商应该这样做,因为他们应该从不信任客户端(例如XSS和中间人攻击)。你怎么知道请求没有截获?验证一切。
有效有几个级别:
只有提供者方可以做#2和#3。
答案 1 :(得分:19)
对于API,被调用方应始终进行适当的验证,并为无效数据抛出描述性异常。
对于任何具有IO开销的客户端,客户端也应该进行基本验证......
答案 2 :(得分:11)
验证:来电与被叫
TLDR版本都是。
长版本涉及谁,为什么,何时,如何以及什么。
<强>两个强>
两者都应该准备好回答问题&#34;这些数据是否可以可靠地运行?&#34; 我们是否对这些数据有足够的了解来做一些有意义的事情?许多人会建议数据的可靠性永远不可信,但这只会导致鸡和蛋的问题。从两端无休止地追逐它不会提供有意义的价值,但在某种程度上它是必不可少的。
两者都必须验证数据的形状以确保基本可用性。如果任何一方无法识别或理解数据的形状,就无法知道如何以任何可靠性进一步处理它。根据环境的不同,数据可能需要是特定的“类型”,这通常是验证形状的简便方法。我们经常考虑将具有共同线索的证据的类型反馈给特定的祖先并保留具有正确形状的关键特征。如果数据不是内存结构中的任何内容,则其他特征可能很重要,例如,如果它是流或运行上下文之外的其他资源。
许多语言通过类型或接口检查将数据形状检查作为内置语言功能。然而,当有利于组合而不是继承时,提供一种良好的机制来验证特质存在是实施者的责任。实现这一目标的一个策略是通过动态编程,或者特别是通过类型内省,推理或反思。
<强>调用强>
被调用者必须验证它将操作的给定上下文的域(输入集)。被调用的设计总是表明它只能处理很多输入的情况。通常将这些值分解为某些子类或输入类别。我们在被调用域中验证域,因为被调用与局部约束密切相关。它比任何人都更清楚什么是好的输入,什么不是。
正常值:域的这些值映射到范围。对于每个foo
,只有一个bar
。
超出范围/超出范围值:这些值是一般域的一部分,但不会映射到被调用上下文中的范围。这些值不存在已定义的行为,因此无法生成有效输出。经常超出范围的检查需要范围,限制或允许的字符(或数字或复合值)。基数检查(多重性)和随后的存在检查(空或空)是范围检查的特殊形式。
导致不合逻辑或未定义行为的值:这些值是特殊值或边缘情况,否则正常,但由于算法设计和已知的环境约束,会产生意外的结果。例如,对数字进行操作的函数应该防止被零除或会溢出的累加器,或者意外地失去精度。有时操作环境或编译器可以警告这些情况可能发生,但依赖于运行时或编译器并不是一种好的做法,因为它可能并不总是能够推断出可能的和不可能的。这个阶段应该通过二次验证在很大程度上验证呼叫者提供了良好,可用,有意义的输入。
<强>呼叫者强>
来电者很特别。调用者有两种情况应该验证数据。
第一种情况是赋值或显式状态更改,其中通过某种显式机制在内部或外部由容器中的某些内容对数据的至少一个元素进行更改。这有点超出了问题的范围,但需要记住一些事项。重要的是在发生状态变化时考虑上下文,并且描述状态的一个或多个元素受到影响。
第二种情况是数据调用函数。呼叫者只能从被呼叫者那里得到这么多。呼叫者必须知道并尊重被叫只识别某个域。调用者也必须是自私的,因为它可能会继续并在被叫完成后持续很长时间。这意味着调用者必须帮助被调用者不仅成功,而且还适合于任务:坏数据会产生不良数据。同样地,就调用者而言,即使是关于被调用者的良好数据输入和输出也可能不适合于接下来的事情。好的数据输出实际上可能是调用者的错误数据。被叫的输出可能使呼叫者的当前状态无效。
好的,如此充分的评论,调用者应该具体验证什么?
逻辑和正常:给定数据,被称为符合目的和意图的好策略?如果我们知道它会因某些值而失败,那么在没有适当警卫的情况下执行呼叫是没有意义的。如果我们知道被叫不能处理零,不要问它,因为它永远不会成功。什么更昂贵,更难管理:[冗余(我们知道吗?)]保护条款,还是[可能长期运行,外部可用的资源依赖流程后期发生的异常]?实现可以改变,并突然改变。在呼叫者中提供保护可以减少改变实施的影响和风险。
返回值:检查完成失败。这是呼叫者可能需要或可能不需要做的事情。在使用或依赖返回的数据之前,如果系统设计包含可能伴随实际返回值的成功和失败值,请检查替代结果。
脚注:如果不清楚的话。 Null是一个域名问题。它可能是也可能不是逻辑和正常的,所以它取决于。如果null是函数的自然输入,并且可以合理地期望函数产生有意义的输出,则将其留给调用者以使用它。如果调用者的域是null不合逻辑的,那么在两个地方都要防止它。
一个重要的问题:如果你将null传递给被调用者,并且被调用者正在产生某种东西,那么这不是一个隐藏的创造模式,从无到有创造出什么东西吗?
答案 3 :(得分:9)
这都是关于“合同”的。那是一个被调用者,决定哪些参数是好的。
您可以在文档中输入“null”参数无效,然后抛出NullPointerException
或InvalidArgumentException
就可以了。
如果返回null参数的结果有意义 - 在文档中说明它。通常这种情况是糟糕的设计 - 使用较少的参数创建覆盖方法,而不是接受null。
只记得抛出描述性异常。根据经验:
NullPointerException
或InvalidArgumentException
)getPermissionsForUser(Integer userId)
,调用者通过userId
不知道这样的用户是否存在但是它是非空的整数。您的方法可能会返回权限列表或者UserNotFoundException
。它可能是一个经过检查的例外。答案 4 :(得分:4)
嗯......这取决于。
如果您可以确定如何处理被叫方内的无效数据,那么就在那里进行。
如果您不确定(例如,因为您的方法非常通用并且在几个不同的地方和方式中使用),那么让调用者决定。
例如,假设一个DAO方法必须检索某个实体而你却找不到它。你能决定是否抛出异常,可能会回滚一个交易还是只考虑一下? 在这种情况下,决定如何处理它肯定取决于调用者。
答案 5 :(得分:4)
两者。这是双方良好的软件开发问题,独立于环境(C / S,Web,内部API)和语言。
被调用方应根据记录良好的参数列表验证所有参数(您确实记录了它,对吧?)。根据环境和体系结构,应实施良好的错误消息或异常,以明确指出参数的错误。
调用者应确保在api调用中仅传递适当的参数值。应尽快捕获任何无效值,并以某种方式反映给用户。
正如生活中经常发生的那样,任何一方都不应该假设另一个人会做正确的事并忽略潜在的问题。
答案 6 :(得分:4)
取决于你是在名义上,防守上还是完全上场。
在大多数情况下,您可以采用防御性(快速失败)或完全(失败安全)进行编程。名义编程是有风险的IMO,在期望来自外部源的输入时应该避免。
当然,不要忘记记录所有内容(特别是在名义上编程时)。
答案 7 :(得分:3)
我将对这个问题采取不同的看法。在包含的应用程序内部工作,调用者和被调用者都使用相同的代码。然后,被调用者的合同所要求的任何验证都应由被调用者完成。
所以你写了一个函数,你的合同说:“不接受NULL值。”您应该检查是否尚未发送NULL值并引发错误。这可以确保您的代码是正确的,如果其他人的代码正在做某些事情,那么他们就不应该早点了解它。
此外,如果您认为其他代码会正确调用您的方法,但他们没有,会使跟踪潜在错误的来源更加困难。
这对"Fail Early, Fail Often"至关重要,因为我们的想法是在检测到问题后立即引发错误情况。
答案 8 :(得分:2)
称为责任。这是因为只有被调用知道什么是有效的。这也是一种很好的安全措施。
答案 9 :(得分:2)
它也需要在客户端和服务器(被叫者和呼叫者)两端都是。
客户:
服务器:
总的来说, 1.如果数据来自UI,则最好在UI层验证并在服务器层进行双重检查。 2.如果在服务器层本身进行数据传输,我们需要在被调用者上进行验证并进行双重检查,我们也要求在调用方面进行。
由于
答案 10 :(得分:2)
在我的拙见中,以及在解释原因的几个词中,大部分时间都是被调用者的责任,但这并不意味着来电者总是毫无需要。
原因是被调用者最有能力知道它需要做什么工作,因为它是做这项工作的人。因此,对象或方法的自我验证是良好的封装。如果被调用者无法对空指针执行任何操作,那么这是一个无效的参数,应该被抛回原样。如果有超出范围的参数,那么也很容易防范。
然而,“无知法律不是防御”。对于调用者而言,简单地将所有内容都推送到其辅助函数中并让被调用者对其进行排序并不是一个好的模式。调用者在执行此操作时不会添加任何值,首先,特别是如果调用者推入被调用者的是数据,它本身由其自己的调用者给出,这意味着调用堆栈的这一层可能是多余的。它还使调用者和被调用者的代码都非常复杂,因为双方“防御”了另一方的不受欢迎的行为(被调用者试图挽救可行的东西并测试所有内容,并且调用者包含调用尝试纠正调用的try-catch语句。
因此,调用者应该验证它能够了解传递数据的要求。当进行调用时固有的时间开销(例如调用服务代理时)时尤其如此。如果你必须等待很长一段时间才能发现你的参数是错误的,当需要几个滴答来做相同的客户端时,优势显而易见。被调用者的守卫条款就是这样;在丑陋的东西被扔出实际的工作程序之前,最后一道防线和优雅的失败。
答案 11 :(得分:0)
调用者和被调用者之间应该有一种被称为契约的东西。如果输入数据是指定值,则被调用者确保它做正确的事情。他仍然应该根据这些规范检查输入数据是否正确。在Java中,您可以抛出InvalidArgumentException
。
来电者也应该在合同规范范围内工作。如果他应该检查他移交的数据取决于案件。理想情况下,您应该以检查是否必要的方式对调用者进行编程,因为您确定数据的有效性。如果它是例如用户输入您无法确定它是否有效。在这种情况下,你应该检查它。如果你没有检查它,你至少必须处理异常并做出相应的反应。
答案 12 :(得分:0)
被调用者有责任检查收到的数据是否有效。如果不能执行此任务,几乎肯定会导致软件不可靠,并使您面临潜在的安全问题。
如果您已经控制了客户端(调用者)代码,那么您还应该至少执行一些验证,因为它会带来更好的整体体验。
作为一般规则,尽量尽早发现数据问题,这样可以减少进一步的麻烦。