何时在API中使用HTTP状态代码404


58

我正在做一个项目,在与工作人员争论了一个多小时之后。我决定知道堆栈交换中的人会说些什么。

我们正在为系统编写API,有一个查询应返回组织树或目标树。

组织树是用户所在的组织,换句话说,该树应始终存在。在组织中,应该始终存在目标树。(这是参数开始的地方)。如果树不存在,我的同事决定以状态码200回答响应是正确的。然后开始要求我修复我的代码,因为没有树时应用程序就崩溃了。

我会尽力避免火焰和愤怒。

我建议在没有树的情况下引发404错误。这至少会让我知道出了点问题。使用200时,我必须在成功回调中的响应中添加特殊检查以处理错误。我期望收到一个对象,但实际上可能会收到一个空响应,因为未找到任何内容。将响应标记为404听起来很公平。然后战争爆发了,我收到一条消息,说我不理解HTTP状态代码架构。所以我在这里,问在这种情况下404有什么问题?我什至得到了“它什么也没发现,所以返回200是正确的”的说法。我相信这是错误的,因为树应该一直存在。如果我们什么也没找到,并且期望得到结果,则应该是404。

更多信息,

我忘记添加获取的网址。

组织机构

/OrgTree/Get

目标

/GoalTree/GetByDate?versionDate=...
/GoalTree/GetById?versionId=...

我的错误,两个参数都是必需的。如果提供了可以解析为日期的任何versionDate,它将返回关闭修订。如果您输入过去的内容,它将返回第一个修订版。如果按ID标识的ID不存在,我怀疑它将返回200的空响应。

额外

另外,我认为,对这个问题的最佳答案是在创建组织时创建默认对象,没有树不应该是有效的情况,应视为未定义的行为。没有两棵树就无法使用一个帐户。因此,它们应该始终存在。

我也链接了这个(一个相似但我找不到)

http://viswaug.files.wordpress.com/2008/11/http-headers-status1.png


请说明:如果没有树,如果总是有前提条件的话,应用程序如何崩溃?(我同意您的意见,看起来像是404)
Andres F.

好的代码没有检查null,而是将json字符串解析为一个对象。在代码中的某个位置,“被”加载的对象不存在,因为无法在内部找到它。
卢瓦克福雷-Lacroix的

4
如果您提供要访问的资源的URI,那就更清楚了。如果是/ goals /,则返回200,并返回空集。如果您尝试访问/ goals / {goal_id},则返回404。如果您返回404要求/ goals /的请求,则意味着URI不存在,因此不应再使用。
imel96

1
在这两种情况下,问题仍然存在。应该/GoalTree/GetById?versionId=CompletelyInvalidID返回什么?不成功,因为/GoalTree/GetById?versionId=CompletelyInvalidID实际上找不到命名的资源。

2
太好了,现在讨论已经从您的工作转向了互联网!现在势不可挡!
卡洛斯·坎德罗斯(CarlosCampderrós)

Answers:


79

如有疑问,请查阅文档。查看W3C的HTTP状态代码定义,可以得出以下结论:

200 OK-请求成功。响应返回的信息取决于请求中使用的方法。

找不到404-服务器未找到与请求URI匹配的任何内容。

在您的API上下文中,它很大程度上取决于如何创建查询以及如何检索对象。但是,我的解释一直是:

  • 如果我要求一个特定的对象,并且它存在返回200代码,如果不存在,则返回正确的404代码。
  • 但是,如果我要请求一组与查询匹配的对象,则空集是有效的响应,并且我希望返回的是200代码。这样做的理由是查询有效,查询成功且查询未返回任何内容。

因此,在这种情况下,您是正确的,该服务不是在搜索 “特定的事物”,而是在请求特定的事物,如果找不到该事物,请说清楚。

我认为维基百科说得最好:

200 OK-...实际响应将取决于所使用的请求方法。在GET请求中,响应将包含与所请求资源相对应的实体。

找不到404-找不到请求的资源,但将来可能再次可用。客户的后续请求是允许的。

对我来说似乎很清楚。

关于示例请求

/GoalTree/GetByDate?versionDate=...
/GoalTree/GetById?versionId=...

您说过,对于格式,您总是返回最接近该日期的修订版。它永远不会返回对象,因此它应该一直在返回200 OK。即使这可以采用某个日期范围,并且逻辑将返回该时间范围内的所有对象,但返回200 OK-0结果就可以了,因为那是请求的目的-满足该条件的一组事物。

但是,后者与您要的是具有该标识的特定对象(可能是唯一的)不同。200 OK在这种情况下返回错误,因为请求的资源不存在并且找不到

关于选择状态码

  • 2xx代码告诉UA 它做了正确的事情,该请求有效。将来可以继续这样做。
  • 3xx代码告诉UA,您曾经问过的可能是什么,但是现在其他地方了。将来,UA可能会考虑只进行重定向
  • 4xx代码告诉UA 它做错什么,它构造的请求是不正确的,应该至少在没有进行任何修改的情况下再试一次。
  • 5xx代码告诉UA 服务器以某种方式损坏。但是请注意,该查询将来可能会起作用,因此没有理由不再次尝试。(除了501,这是400期的更多内容)。

您在使用5xx代码的注释中提到,但是系统正在运行。询问了一个无效的查询,需要将该查询传达给UA。无论如何切片,这都是4xx区域。

考虑一个外星人质疑我们的太阳系

外星人:计算机,请告诉我人类居住的所有星球。

电脑:找到1个结果。地球

外星人:计算机,请告诉我有关地球的信息

电脑:地球-大部分无害。

外星人:计算机,请告诉我有关小行星带以外人类居住的所有行星的信息。

计算机:找到0个结果。

外星人:计算机,请摧毁地球。

计算机:200 OK。

外星人:计算机,请告诉我有关地球的信息

电脑:404-找不到

外星人:计算机,请告诉我人类居住的所有星球。

计算机:找到0个结果。

外星人:伟大的艾肯帝国的胜利!


4
+1这不是不返回任何结果的查询。这就像向浏览器询问已知的网页却找不到它。正是404的用途。
Andres F.

2
@ imel96会忘记查询字符串是URL的一部分。
卢瓦克福雷-拉克鲁瓦

1
@LegoStormtroopr您可笑的“外星人”示例起作用,因为当地球不存在时,宇宙不是无效的。但是按照OP的解释,他的系统必须包括树。没有树,该系统将无法正常工作。
Andres F.

1
@LegoStormtroopr想象一个数据库表。您查询表,有时会得到结果,有时却没有。表是您的资源,无论是否返回行,它始终存在。该表是可识别的,它具有名称(例如http资源具有URI)。这些行不是,它们仅匹配某些参数。即使在数据库中,如果执行不匹配的更新,您也会收到“确定的0行受影响”。
imel96

2
@LegoStormtroopr您已经有了答案。如果他们想重新映射/ GoalTree / GetById?versionId = x,则它应返回301,并将Location标头设置为/ GoalTree / Id / x。
imel96 2013年

11

忽略/ GoalTree / Get *看起来像动词而不是资源的事实,您应该始终返回200,因为URI / GoalTree / Get *表示始终可访问的资源,并且如果没有树,则不是客户端错误。一个要求。当没有实体要返回时,只需返回200且设置为空即可。

如果找不到资源,则使用404,而不是没有实体时使用。

换句话说,如果您想为对象返回404,则为它们提供自己的URI。


1
嗯 这很有道理。404是用户错误,但是正如OP所解释的,这实际上是系统错误;用户的请求是完全有效的!我不同意200是正确的答案,因为“无树”是错误
Andres F.

@ imel96我希望总是返回有效的实体,而不是空/状态码4xx / 5xx。如果只是我,我将返回一个有效的实体,例如Wiki。减少头痛,无需处理错误。实际上,我想说它很像500。系统处于未定义状态,不应该发生。而且返回OK没有意义。关于RFC的404也没有意义。因此,当没有任何意义的时候……只有500才有意义!
卢瓦克福雷-拉克鲁瓦

@Sybiam好,您要求输入http状态代码,它的定义非常明确。在这方面,即使您的业务逻辑说这是一个错误,也不意味着作为http服务器的系统有错误。对于您而言,它可以理解您的请求,可以处理您的查询,并且碰巧结果是没有实体。因此,您也不能使用500。至少应考虑为您的对象提供适当的URI,并查看rfc是否更有意义。
imel96

+1如果您有REST API(每个实体都有其自己的路径),则可以返回404,但是您的路径是动词,将始终被找到。
OrangeDog

@OrangeDog:/GoalTree/GetById?versionId=12345 一个非常好的URI(至少是一个相对的URI),它标识特定的资源,即12345与系统中版本ID相对应的数据。如果不存在具有此类ID的数据,则404 HTTP响应非常合适。当然,在任何情况下,响应主体都应包含适当格式的响应(例如JSON,如果这是请求此类资源的典型客户端所期望的响应),则应指示错误的具体性质和原因。
Ilmari Karonen

7

这是一个有趣的问题,因为这全都与系统的规范有关。

imel96的回答已使我确信404不是一个正确的回答,因为4xx系列代码主要是针对用户/客户端错误的,而事实并非如此。URL格式正确,树必须在其中;如果不是,则表明系统处于不一致状态!

因此,这是服务器错误,即5xx系列中的某个错误。可能是一般性的500内部服务器错误或503服务不可用(该服务“正在向我获取必须存在的树”)。


2
不正确,用户出错,因为他们要求的东西不存在

@LegoStormtroopr询问不存在的内容并不总是错误。如果您请求网络资源,并且网络已关闭,则是网络错误。
Andres F.

1
@LegoStormtroopr此外,该树必须存在;根据OP的说明,没有它,系统将无法运行。因此,请求此资源是有效的。如果不存在,则必须是系统(或服务器)错误。
Andres F.

2
@Sybiam如果您打算使用5xx代码,则503表示“ 503服务不可用-由于服务器暂时超载或维护,服务器当前无法处理该请求”。您的服务器没有过载,没有找到请求。另外,当“服务器知道它已错误或无法执行请求”时,使用5xx代码

1
@AndresF。老实说,500码可能没问题。考虑到问题如何随着时间而变化,它会起作用。通常,如果一切都不好,我只是反对返回200。

6

我想说200或404响应码都可以有效,具体取决于您如何看待这种情况。

问题是HTTP响应代码是在服务器的上下文中定义的,它可以根据URL 传递各种资源。在这种情况下,的含义200 OK404 Not Found是完全明确的:前者说:“这就是你请求的资源”,而后者则说“对不起,我没有这样的任何资源。”

但是,根据您的情况,您将在HTTP服务器和所请求的实际资源(树)之间有一个附加的应用程序层。该应用程序占用了某种中间空间,而该空间在HTTP规范中没有得到很好的解决。

从网络服务器的角度来看,应用程序看上去那种像一个资源:它通常在服务器上的文件,通过标识(的一部分)的URL,就像其他资源(例如静态文件)的服务器可能会任职。另一方面,它是一种奇怪的资源,因为它由可执行代码组成,可以动态确定响应的内容,甚至可能甚至是状态代码,从而使其在某种程度上表现得像小型服务器。

特别是,在您的示例情况下,Web服务器可以很好地定位应用程序,但是应用程序随后无法定位已请求的子资源(树)。现在,如果您认为应用程序只是服务器的扩展,而子项目(树)是实际的资源,则可以使用404响应:服务器只是将寻找实际资源的任务委托给了应用程序,但最终未能做到。

另一方面,如果您的观点是应用程序是所请求的资源,那么显然Web服务器应该返回200响应;毕竟,该应用程序已找到并正确执行。显然,在这种情况下,应用程序实际上应以预期的格式返回有效的响应主体,从而指示(使用格式编码的任何高级协议)未找到与查询匹配的实际数据。

这两种观点都是有道理的。 在大多数情况下,至少对于打算使用普通Web浏览器通过HTTP直接访问的应用程序,我倾向于前一种观点:用户通常不关心内部细节,例如服务器和应用程序之间的区别,它们只是关心他们想要的数据是否存在。

但是,在设计为与使用自定义的高级API协议其他计算机程序,进行通信的应用的特定情况下使用HTTP仅作为一个低级别的传输层,有利于以制成一个参数后一种观点:对与此类应用程序接口的客户端,在HTTP级别上,他们真正关心的只是他们是否成功与应用程序联系。在这种情况下,通常使用更高级别的协议更自然地传达其他所有信息。


无论如何,无论您偏爱以上哪种视图,都应牢记一些细节。一个是,在许多情况下,(基本上)资源和不存在的资源之间可能存在有意义的区别。

在HTTP级别上,一个空资源将简单地由200个响应代码和一个空响应主体表示,而一个不存在的资源将由404响应和解释该资源不存在的资源主体表示。在更高级别的API协议中,通常会通过错误响应来指示不存在的资源,其中包含适当的协议特定的错误代码/消息,而空响应仅是没有数据项的正常响应结构。

(请注意,从我上面的意义上讲,资源不必为“空”而实际上就应该为零字节。例如,没有匹配项的搜索结果在广义上将被视为空,而带有没有行或没有实际数据的XML文档。)

另外,当然,如果应用程序确实相信所请求的子资源应该存在,但找不到,那么可能存在第三个响应代码:500 Internal Server Error。如果资源的存在是应用程序的假定前提,则这样的响应是有意义的,因此,资源的缺失必然表示内部故障。

最后,您应该始终牢记Postel定律

对发送的内容要保守,对接收的内容要宽松。

服务器是否在特定情况下以200或404响应进行响应,这并不能使作为客户端实现者来适当地处理任一响应并以最大程度地实现强大的互操作性。当然,在不同情况下“适当的”处理的含义是可以争论的,但通常不应包括崩溃或“崩溃”。


困境得到了很好的解释。
Marcel

没有两难选择。这个答案不是基于相关的RFC中定义的资源。看到我在@LegoStormtroopr答案下的评论。
imel96

@ imel96:我认为您在误解RFC 1630:您在前面的注释中引用的段落完整阅读:“问号(“?”,ASCII 3F十六进制用于界定可查询的URI之间的边界对象,以及用于表达对该对象的查询的一组单词。使用此格式时,组合的URI代表对象,该对象是通过将查询应用于原始对象而得出的。(强调我的意思)。因此,很明显,查询字符串确实是URI的一部分(即使查询字符串之前的部分本身必须是有效的URI)...
Ilmari Karonen

...并且该组合的URI标识了客户端可以通过将该URI发送到服务器来请求的特定资源。无论如何,RFC 2616(HTTP)都将资源定义为“可以由URI标识的网络数据对象或服务,如3.2节中所定义”。并接着说:“就HTTP而言,统一资源标识符是简单格式化的字符串,它们通过名称,位置或任何其他特征来标识资源。”
Ilmari Karonen

@IlmariKaronen你是对的。我已经将HTTP与REST混淆了。不过,这似乎仍然不正确,因为我不确定您如何使用URI这样的资源(例如/ GoalTree / Get?versionDate = 2000BC)
imel96

3

204 No Content怎么样?这表明您的请求已成功处理,但未返回任何内容。它仍然是“成功”,但可以让您查看是否仅基于状态码获得了结果。


6
如果您进一步阅读该规范,则“此响应主要是为了允许进行操作的输入而不会导致更改用户代理的活动文档视图”。因此,不应将其用于GET请求。
2013年

3

如果URL表示不存在的资源,则返回404 Not Found

如果URL表示资源为空列表,则返回一个空列表,然后单击200 OK。

例:

{
  total: 0,
  items: []
}

如果URL表示曾经存在的资源,则返回410 Gone。

关于Lego Stormtrooper的对话框:

Alien: Computer, please tell me all planets that humans inhabit. GET /planets?inhabitedBy=humans

Computer: 200 OK. { total: 1, items:[{name:'Earth'}] }

Alien: Computer, please tell me about Earth. GET /planets/earth

Computer: 200 OK. {name:'Earth', status: 'Mostly Harmless'}

Alien: Computer, please tell me about all planets humans inhabit, outside the asteroid belt. GET /planets?inhabitedBy=humans&distanceFromSun=lots

Computer: 200 OK. {total:0, items:[] }

Alien: Computer, please destroy Earth. DELETE /planets/earth

Computer: 204 No Content. (or 202 Accepted if it takes some time to destroy Earth)

Alien: Computer, please tell me about Earth. GET /planets/earth

Computer: 410 Gone

Alien: Computer, please tell me all planets that humans inhabit. GET /planets?inhabitedBy=humans

Computer: 200 OK 0 {total: 0, items:[] }

Alien: Victory for the mighty Irken Empire!

1

从它的声音来看,这是内部使用的API 。无论使用哪种书本(规范),这都会带来使用优势最大的优势。这并不意味着完全发明自己的状态代码,但是如果有益的话,可以稍微弯曲一下规则。

我同意您的立场,即应该获得一个状态代码,以表明发生了问题。这毕竟是状态代码的用途。您还可以从引发异常/等的库中受益。使用非200状态代码,因此您不必显式检查(也可以编写自己的包装程序来执行此操作)。

我也同意安德列斯·F(Andres F.)的观点,认为500是适当的,因为树应该存在。不过在实践中,我喜欢将服务器错误分为两类。一些意想不到的错误,因此东西,我几乎可以检查出了问题。结果为以下状态码,

  • 200-一切都很好
  • 404-错误的网址
  • 409-出问题了
  • 500-服务器上发生意外错误

在您的特定情况下,您可以检查树在服务器端是否存在,如果不存在,则返回409。这是预期的错误(您知道可能会发生,可以进行检查等)。 。409冲突只是我个人的喜好,只要您可以坐下来与您的团队决定,那么5xx也可能合适。

像这样对代码进行分类可以帮助您更快地识别错误的类型,但是它可以带来超出组织范围的好处。通常由于网站错误,您不希望客户端遇到意外错误,因为这可能是安全问题,并且会显示漏洞,因此您返回通用500“发生错误”。并在服务器上记录完整错误。但是,如果409发生了预期的错误,您就知道可以将错误显示给客户端,并且您不必就所发生的事情将它们蒙在鼓里。这只是我可以叙述的一种实际用途,但是有很多可能性。

这有点让人难以接受,因为您由于无法与同事达成共识而发布了此帖子,但这听起来好像你们正在更多地讨论语义以及谁在政治上是正确的。只要您能提出对公司最有利的系统,谁才是更合适的人并不重要。

另一方面,如果这是一个公共API,则遵循尽可能接近的规范将更为重要,以避免社区之间的混淆。


0

采取切入点的方法:如果一个人最终(通过GUI)使用API​​,我建议您尽一切努力使最终用户的生活变得轻松。当该树应存在时,该树不存在是“域模型不一致”错误。系统错误是指内存不足或发生其他系统故障时。因此,返回5xx是不合适的。如以上几个人所提到的,如果树本身具有自己的URI,则4xx可能是合适的,在此情况并非如此。但是这是404告诉客户的:您可以一次又一次尝试,直到您得到一些回报。如果返回200,则可以将足够的诊断信息返回给用户或用户代理,以便用户代理可以显示消息,以便用户停止重试并仅与支持部门联系。另一方面,如果此API仅用于系统,


404的全部意思是“那个东西不在这里,我不知道它在哪里”。3xx和5xx可以重试。但是4xx说:“您当前的查询对我来说不足以为您找到肛门炎。请走开。”

我喜欢区分URL NOT FOUND和URL NOT FOUND的可能性...因此,服务端点已启动并正在运行200,但是请求的资源却未找到404(响应正文)。
Lemonade
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.