服务器是否应该“宽容”它所接受的内容并“静默丢弃错误的输入”?


27

我的印象是,到现在为止,每个人都同意这句话是错误的。但是我最近看到了这个答案,其中有一个“宽容”的评论被投票了137次(截止到今天)。

在我看来,宽容浏览器的原因是HTML和其他一些Web标准在几年前就完全混乱的直接原因,并且直到最近才开始从该混乱中明确体现出来。我的看法是,宽容您所接受的东西导致这种情况。

格言的第二部分是“以静默方式丢弃错误的输入,除非规范要求,否则不返回错误消息”,这使人感到不快。任何在某些问题无声地失败时撞墙的程序员都会知道我的意思。

那么,我对此完全不对吗?我的程序是否应该宽容它所接受的内容并默默地吞下错误?还是我误解了这意味着什么?


最初的问题是“程序”,我对此表示赞同。宽大处理程序是有意义的。但是,我真正的意思是API:暴露给其他程序而非人的接口。HTTP就是一个例子。该协议是仅其他程序使用的接口。人们从不直接提供诸如“ If-Modified-Since”之类的标题中的日期。

因此,问题是:除标准实际要求的格式外,实施一项标准的服务器是否应宽松,并允许采用其他几种格式的日期?我认为,“宽容”应该适用于这种情况,而不是人机界面。

如果服务器是宽松的,这似乎是一个整体的改进,但是我认为在实践中,它只会导致客户端实现最终依赖宽大处理,因此无法以稍有不同的方式与另一台宽松的服务器一起工作。

那么,公开某些API的服务器应该宽容还是一个非常糟糕的主意?


现在开始宽大处理用户输入。考虑使用YouTrack(错误跟踪软件)。它使用的文字输入语言让人联想到Markdown。除了“宽大”。例如写

- foo
- bar
- baz

不是创建一个项目符号列表的文档的方式,但它的工作。因此,它最终在我们的内部Bugtracker中被大量使用。下一个版本问世了,这个宽大的功能开始工作时略有不同,打破了一堆(误用)此(非)功能的列表。当然,创建项目符号列表的文档化方法仍然有效。

那么,我的软件应该宽容接受的用户输入吗?


4
关于“以静默方式丢弃错误的输入”,我会询问每种情况应该视为错误的输入。如果您问用户一个问题,并且期望“是”或“否”,那么输入“ YES”是否是错误的?“ y”呢?oui怎么样?通常,不要害羞地告诉用户输入的内容不是您期望的。但是,请确保您尽可能做到包容-在我看来,这就是“宽容”的意思。

3
如果您谈论的是最终用户输入-与您的应用程序的用户友好度有关,那么您应该保持警惕。对于自动的机器生成的输入(来自API),您应该冗长(严格)。
Burhan Khalid 2012年

2
实际上,宽大处理是HTML如此流行的原因(以及XHTML的严格性是为什么丢弃了HTML)。
奥利弗·韦勒

1
我认为关键是,如果可以允许它正常失败,那么至少要记录该事件。
钻机2012年

2
@OliverWeiler我认为XHTML的失败与完全不需要它有关。HTML已经在那里,还可以工作。另外,尽管HTML当然非常流行,但我们称这项技术为成功却令人遗憾。它可以满足需求,但是可以满足Symbian满足对智能手机的需求的需求。
罗曼·斯塔科夫

Answers:


9

当然你是完全正确的。程序永远不应“宽大”,因为这样做只会掩盖问题。应该突出问题,而不是扫除问题。信息错误消息是程序对用户有帮助的绝对必要条件。

在大多数情况下,提供错误/无效数据时,该数据的提供者(无论是用户还是其他程序的输出)可能都不知道它是无效的。吞下错误会使他们相信它是(或可能是)有效的,这会扩散无效数据。

系统进行互操作的唯一方法是完全明确地定义该互操作。程序接受规范外的数据,即使该数据在规范中是无效的,也实际上会接受该数据,这不仅使兼容性变得困难重重,而且也不再正式定义。现在,程序本身就是事实上的标准。因此,宽大的程序无法进一步开发或替换,因为您无法对其操作方式进行最细微的更改。


25

我认为这完全取决于您的目标人群。如果是程序员,那绝对不是。您的程序应该会失败并尖叫流血的谋杀案。但是,如果您的目标受众不是程序员,那么您的程序应该宽容一些,以便可以优雅地处理异常,否则,会悄悄地流血。

作为案例研究,以NPAPI Flash Player为例。有一个“发布”版本供那些不太在意可能发生的错误的99%的人使用,但是还有一个“调试”版本,可以在发生任何错误时尖叫流血的谋杀。每个明显支持播放Flash内容,但针对两个完全不同的受众特征。

最后,我认为重要的是:您的用户关心什么?


4
绝大多数声称程序员之外拥有目标受众的Unixy命令行工具对于犯错误的用户毫无用处。即使您不是程序员,通常一个程序解释一个问题比做一些荒谬或无意识的事情更好。
Timwi 2012年

2
@romkyns:不完全是,我是说您的应用程序应该以对目标用户有意义的方式处理错误。
Demian Brecht 2012年

@Timwi:在这种情况下,那些Unixy命令行工具的体系结构很差;)
Demian Brecht

3
@romkyns-我认为一个很好的例子是:在调试模式下,您希望程序停止任何问题并告诉您出了什么问题。在生产模式下,您希望程序尽可能继续运行,并记录其可以处理的所有问题。这样,程序员可以看到他们做错了什么并加以解决,但是用户不会为他们无法解决的事情所困扰。有些问题显然无法解决,但是CSS样式规则就是一个很好的例子,即使您不了解其中一种样式规则,仍可以在其中呈现网站。
恢复莫妮卡2012年

1
@BrendanLong的评论非常令人震惊-有时产生输出比正确更重要。无需用户输入,就可以正常恢复一些错误(或警告);在这些情况下,由您决定要让应用程序做什么。
丹尼尔·B

7

有两种“宽大”的类型:一种是接受不正确的输入并尝试理解它,另一种是接受不同类型的输入。

通常,您总是希望在可行时使用第二个。第一个是当你快速而艰难地死亡时。例如:日期。

这是一些示例输入,包括有效,无效和模糊。

  • 2011-01-02
  • 01/02/2011
  • Jan 2, 2011
  • 2-Jan-2011
  • Green

这里只有一个无效的输入: Green。甚至不要尝试接受它作为日期。由于Green显然不是日期,因此可以接受静默故障。

01/02/2011是有效的,但不明确。您不一定知道是否已将其输入为美国日期(1月2日)(2月1日)。在这里,最好是大声失败,并询问用户明确的日期。

2011-01-02通常被认为是明确的,因此继续进行下去并假设其格式为“ YYYY-MM-DD”通常会很好,并且只会失败。但是,在处理用户输入时,这有点判断力。

Jan 2, 2011并且2-Jan-2011是有效和明确的,应该接受它们。但是,The Second of January of the year 2011也是有效且明确的,但是为了宽大处理而走那么远是过分的。继续前进,然后像它一样默默地使它失败Green

简而言之,答案是“取决于”。看一下可以输入的内容,并确保您永远不会接受冲突的输入类型(例如DD/MM/YYYYvs MM/DD/YYYY)。

在链接的问题/评论的上下文中,情况为2011-01-02。输入类似于JSON,即使mimetype错误,输入也将像JSON一样进行验证;继续尝试并使用它,即使它在线下的某个时刻失败了。


1
您在这里没有考虑一件事。如果用户键入了该字符串,那么我应该接受各种格式,对此毫无疑问。但是我们谈论的是API。API的客户端是其他程序。如果日期格式比较宽松,那么以后每台公开此API的服务器都必须采用完全相同的宽松方式,否则可能会导致不兼容。宽恕最终有害无益,不是吗?
罗曼·斯塔科夫

1
@romkyns我认为您误会了宽大的位置。该API应该在什么宽松的接受(应该理解所有的2011-01-02Jan 2, 20112-Jan-2011,如果这不是太难以实现),而不是它所输出。该API的未来客户甚至不需要知道任何给定的API,只要他们正确输入了其中一个即可。理想情况下,API层会将所有这些信息转换为代码使用的内部表示形式,然后再进行传递。
Izkata 2012年

@romkyns 例如,输出可以始终为2011-01-02格式,这就是您要在文档中输入的格式。我认为完全没有有害影响。
Izkata 2012年

4
@Izkata:你误会了。想象有一个旧程序,只能以二进制形式使用。您必须编写一个程序,该程序接受与旧程序相同的输入。如果旧程序在接受的内容中定义明确,则您的工作也定义明确。如果宽容,那么您的工作是不可能的。
Timwi 2012年

1
强烈不同意。除非是用户输入的输入,否则始终严格限制输入和输出。当您的服务需要重新实施时会发生什么?您是否记录了所有可能的日期格式?您将需要全部实施它们,因为您不希望老客户破产。请为所有机器生成的日期实例和期间使用ISO 8601:它已被明确指定并且可在库中广泛使用。顺便说一句,2011-01-02到底是什么意思?从第二个00:00到第3个00:00的时间段?在哪个时区?
Dibbeke

6

沉默失败是您有可能做过的最糟糕的事情。您是否尝试调试无提示失败的API?这是不可能的

有“尽力恢复但发送详细错误”和“严重失败”。


3

在我看来,Postel定律-“对您的工作要保守,对别人接受的东西要放宽”,这是JSON服务正在讨论的问题。这通常应用于Web服务,而不是UI。

对于UI的建设性用户反馈和约束用户输入是我们使用的经验法则。


但是,如果您查看此处的答案,每个人似乎都同意,只有对UI才有意义,即与原始法律相反。
罗曼·斯塔科夫

我明白您的意思,并同意张贴者的观点,即干净的严格API /服务是目标,但无论好坏,我都知道我以一种或另一种形式为服务增加了“健壮性”。通常在边界处进行一两个值转换。只要含义很清楚,应用程序就会知道如何处理消息,并且不会违反任何业务规则,那么增加健壮性将有助于互操作性。
MarcLawrence 2012年

直到其他人执行您的规范时,才发现数百个客户所依赖的“健壮性”实际上不在规范中,必须进行反向工程……
Roman Starkov

3

我认为,TAOUP的第1章第6节已对此进行了详细介绍。具体来说,修复规则指出程序应使用输入执行应做的事情,将正确的数据转发给系统,如果正确的响应为失败,则应尽快执行。

类似的概念是防御性编程。您知道将收到什么样的输入,但是您的程序应该足够健壮以涵盖所有情况。这意味着在恢复情况下应针对已知问题(例如输入错误)进行编程,全面处理未知情况。

因此,只要您正在处理该输入,就可以静默丢弃有问题的输入。您绝对不能像现在那样将其放在地板上。


对于API,我认为宽容与程序相同。输入仍然错误,但是您正尝试尽可能地修复。区别在于什么是有效的维修。正如您所指出的那样,宽松的API可能会导致问题,因为人们使用了不存在的“功能”。

当然,API只是组成规则的较低版本。这样,由于它是一个接口,因此它实际上已包含在最少惊讶规则之内

正如斯宾塞(Spencer)的名言所指出的那样,请避免表面上的相似性,这种相似性可被视为“模糊”输入。在这种情况下,我通常会认为一切都指向该程序无法修复,因为它不知道所需的内容,这对于用户群而言最不令人惊讶。

但是,您正在处理具有许多“标准”的日期。有时,这些甚至混合在单个程序(链)中。由于您知道预计会出现日期,因此尝试识别该日期只是一种很好的设计。特别是如果日期来自某个外部程序,并且一秒钟未修改地传递给您。


2

在大多数情况下,部署到服务器的程序应该每分钟甚至有时每秒要接收数千个请求。如果服务器程序接受并纠正来自客户端的错误输入,恐怕它将有两个缺点:

  1. 浪费宝贵的服务器时间。每秒处理1000个以上的请求,检查每个请求中的错误会反映出每个客户端的响应速度很慢。
  2. 对提供正确输入的客户端/客户端程序不公平。除此之外,当服务器端程序员坐在服务器代码上时,他/她必须考虑各种错误输入的情况。谁来决定呢?

服务器程序不应接受错误的输入,但是如果输入错误,则服务器应向客户端返回错误消息。


2

从概念上讲,理想的行为是安全地执行操作,同时确保以某种方式通知可以解决任何问题的人员。当然,在实践中,后者的约束通常不可能直接满足,因此该问题成为处理可疑输入的最佳方法。

在设计协议,格式化规范或“语言”时可能非常有帮助的一件事是,有一种方法可以区分出四类潜在的不可理解的项目:

  1. 如果不了解,应将其过滤掉的事物。
  2. 如果不理解,应该忽略的东西,但是如果需要传递数据,则保留这些东西(也许用某种包装器来表示它已经经过了至少一个不理解的阶段)
  3. 如果不了解,应该会产生警告,但不能阻止尝试进行数据恢复的事物(例如,在网页内,类型未知但文件末尾定义明确的对象)可能显示为红色“ X”,而不会阻止呈现页面的其余部分。)
  4. 那些表明无法理解它们的事物很容易在别处出现严重且不可恢复的问题(例如,指示剩余数据已压缩,并且任何可以执行所需的解压缩的事物都将理解的事物)。

拥有明确定义的约定,通过该约定,可以读取任何数据格式的应用程序将能够识别哪种类别适用于符合更高版本的要求,这比尝试采用临时兼容措施要好得多稍后的。例如,如果文件格式的行格式为“标签:值”,则可以指定任何标签的第一个字符都将指示该标签所属的类别;对于静默忽略类别的标签,可能会有第一个字符还指示该标签对其有效的标准版本(因此,如果声称“静默忽略”标签存在于的版本3中,按照标准,用于版本的解析器会默默地忽略它,但是如果版本3或更高版本的解析器无法解析它,它将发出嘎嘎叫声。

无论如何,最重要的是要避免将模棱两可或误解的数据转换为错误的数据。在某些情况下,最好完全拒绝传播模棱两可的数据,尽管在其他情况下,最好在接收方认为无歧义的情况下准确地按接收的方式传播它。如果不是彻底的邪恶,那么真正危险的是使用不同的假设进行数据转换,例如,转换日期列表,例如:

2012年1月12日
2012年12月13日
12/12/99

像这样的日期列表

2012-01-12
2012-12-13
1999-12-12

即使一个人的名单上有一些错误输入的日期,以原始格式列出的人也有可能确定哪种格式是正确的,并标记可疑的值以供进一步研究(对照其他记录等)。 )以后一种格式显示日期,将使它们毫无希望且无法恢复。


说得好。我喜欢您的答案更深入。我很想接受这一点,但是考虑到这个问题已经引起了人们的普遍兴趣,我想我会保留一段时间。
罗曼·斯塔科夫

1

我的UI经验主要来自台式机系统。网站有所不同,尽管我见过一些可能挑战台式机系统的网站。但是对于它的价值:

我发现错误消息应该是最后的解决方法。理想的系统根本不会有它们。最好的办法是一开始就不允许输入错误:如果用户从几个月的下拉列表中进行选择,则不能输入“绿色”。他无法按灰色按钮。

接下来要做的最好的事情就是接受不良数据。假设您要显示一个月的每日销售额直方图。在用户输入之后,该图形将覆盖一个世纪,而超出一个世纪的竖线则是其他世纪的十倍。现在,用户知道他做错了什么,并futher,知道很多了解他做了什么错比任何消息可以告诉他。当输入是图形化时(例如,通过拖动鼠标),这种反馈仍然有效,而且是无价的。一堆输入可能是无效的,但是使用此方法,用户可以获得有关每个鼠标位置结果的即时详细反馈。

话虽如此,有时用户需要知道为什么按钮变灰,以致于他无法按下按钮。那么,对于它没有帮助(如果,让我知道),但ungray按钮,当他点击它,给他为何按钮没有做什么工作的一个很好的解释。


0

该声明是关于通过互联网发送信息的。通过互联网发送信息的一件事是,信息不一定总能到达目标或分散到目标。


0

这里似乎遗漏了一些东西-失败的后果是什么?

显示网页?您应该尽一切努力来容忍错误的输入。您的选择是显示您可以执行的操作或引发错误。后面的过程不会给用户带来任何好处,因此只能是不得已而为之,因为它会给用户带来完全无用的结果,要使错误更严重,将是非常困难的。

另一方面,如果它要求民兵III的目标,则您会拒绝“莫斯科”作为输入,因为它可能含糊不清。


所以,即使你是一个程序员,你写了一些愚蠢的代码,系统应该继续前进,尽最大努力展示一些东西,而不是只停止并大喊“喂,你搞砸了这里(行号)”?您不认为这正是导致难以置信的错误代码的原因吗?
罗曼·斯塔科夫

@romkyns:对于这类事情,您具有调试模式,这些调试模式严格限制对错误的抱怨。
罗伦·佩希特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.