对于一个有效请求但是没有数据的情况,适当的REST响应代码是什么?

547
例如,您发送了一个GET请求,请求的是users/9,但是没有ID为9的用户。 最合适的响应代码是哪个?
  • 200 OK
  • 202 Accepted
  • 204 No Content
  • 400 Bad Request
  • 404 Not Found

40
提示:你找到第九个用户了吗? - Chris Pfohl
69
那么,用户9 没有被找到 - Tomasz Nurkiewicz
32
@IMB是谁在说204?"No Content"表示你正在寻找的实体存在,但没有呈现。例如,如果id为15的博客没有评论,并且您不想返回空列表以获取博客15的评论:"/blog/15/comments"将返回NoContent。另一方面,如果博客15确实存在,则更适合使用'404 Not Found'。 - Chris Pfohl
17
@Crisfole,你的意思是_“另一方面,如果博客15不存在,则应使用‘404未找到’”_。 - gdoron
22
当然,我做到了 @gdoron! :) 谢谢。可悲的是,我晚了大约三年才能编辑和修复它。 - Chris Pfohl
显示剩余12条评论
29个回答

9

在查看问题后,您不应使用404,为什么?

根据RFC 7231的规定,正确的状态代码是204

在上面的答案中,我注意到了一个小误解:

1.资源是:/users

2./users/8不是资源,这是:路由参数为8的资源/users,消费者可能无法注意到并且不知道差异,但发布者知道并且必须知道!…所以他必须为消费者返回准确的响应。结束。

所以:

基于RFC:404是不正确的,因为已找到资源/users,但使用路由参数8执行的逻辑没有找到任何content可用作响应,因此正确答案是:204

这里的主要观点是:404甚至未找到要处理的资源

204意味着:我找到了资源,已执行逻辑,但是使用您在路由参数中提供的条件未找到任何数据,因此我无法向您返回任何内容。对不起,请验证您的标准并再次调用我。

200:好的,我找到了资源,已执行逻辑(即使我不必返回任何内容),请随意使用它。

205:(GET响应的最佳选项)我找到了资源,已执行逻辑,我有一些内容供您使用,请好好利用。哦,顺便说一下,如果您将在视图中共享此内容,请刷新视图以显示它。

希望这能帮助到您。


2
你从哪里得出这个想法,认为资源是/users而不是/users/8?那是完全错误的。它们都是资源,都由URI(统一资源标识符)表示。 - ruohola
您的答案不正确。"/users/8" 是一个资源,如果没有使用 id=8 标识的用户,或者服务器不想透露该用户的存在,则返回 404 NOT FOUND 是正确的。 - Alexandre V.
/user/8 绝对是一个资源。我更喜欢微软在文档中写的内容。他们解释了RESTful设计作为一种为资源结构化URI的方式。 /users 是一个集合,而 /users/8 则是标识一个项目。两者都是资源。如果集合返回404,则意味着该集合不存在。针对集合的查询(例如添加某些过滤器)可能会导致204或空数组,或带有单个项目的数组。 - infroz

6

6

TL;DR:

  • 如果在/users/9找不到用户,应该返回404。
  • 如果在/users?id=9找不到用户,应该返回204。

详细版:

经过审查我自己对这些状态码的使用以及本文中的示例后,我必须说,在/users/9的URL上没有找到第9个用户时,404是适当的响应。

今天我们的系统里,应用程序洞察日志里充斥着数百条记录的404错误,这让我们的日志变得混乱,所有这一切都是因为当/users/9没有关联数据时,我们决定返回404。但是,这并不意味着我们在设置响应时是错误的,而是我建议这意味着在设置路由时我们的方法是不正确的。

如果您期望一个端点有很高的流量并且担心记录太多的404错误,则应更改路由以符合您要求的状态码,而不是强制使用不恰当的状态码。

我们已经决定对我们的代码进行2个更改:

  1. 更改路由以通过/users?id=9来工作
  2. 将错误代码更改为204,以便404不会填满我们的AI日志。

最终,API的架构师需要了解他们的API将如何使用以及什么样的路由对于该用例是合适的。

我认为,在/users/9的情况下,您请求的资源是用户本身,即第9个用户;您要求服务器响应一个被标识为“9”的对象,该对象恰好存在于一个包含单词'user'的路径中。如果没有找到该对象,则应返回404。

但是,如果调用/users?id=9,我认为您要请求的资源是用户控制器,并提供了更多的具体信息,以便它不返回所有用户的完整列表。您要求服务器响应通过查询字符串中定义的ID号可以识别出的特定用户。因此,如果没有找到任何数据,我认为204是适用的,因为即使没有找到任何数据,控制器也存在。

查询字符串方法还实现了我认为有助于API开发人员和客户端开发人员(特别是初级开发人员或继承此代码或调用该代码的未来开发人员)的东西:

对于任何参与其中的人来说,9都是一个ID而不是任意数字。在这样一个基本的示例中,这一点似乎无关紧要,但考虑一个使用GUID作为行ID或允许您通过人名获取数据的系统,甚至是返回特定邮政编码信息而不是行ID的系统。如果开发人员可以一眼看出标识参数是名字的第一个、最后一个、全名还是邮政编码,那么对所有开发人员都会很有用。


我完全同意你在这里的回答。我写了很多相同的东西,但显然没有发布。就像 /user/8 会产生一个404一样,如果不存在用户9,则 /users/9 应该也是404。如果存在用户8,则 /users?id=8 将返回200,而 /users?id=9 将返回204 Not Found,就像缺少名为“John”的用户时的 /users?Name=John 一样。与空资源非常相似。 - theking2

4

2
Microsoft Azure同样使用404,但是当响应HEAD请求时无法使用自定义错误消息。https://learn.microsoft.com/en-us/rest/api/resources/resources/checkexistence - Mike

3
根据RFC7231 - page59 (https://www.rfc-editor.org/rfc/rfc7231#page-59),404状态码的定义如下:
6.5.4. 404 Not Found 404(未找到)状态码表示源服务器没有找到目标资源的当前表示形式,或者不愿意透露存在该资源。404状态码无法指示此表示形式的缺少是暂时的还是永久的;如果源服务器通过某些可配置的方式知道该条件可能是永久的,则优先使用410(已删除)状态码。默认情况下,404响应可被缓存;即除非方法定义或显式缓存控制另有说明(参见[RFC7234]的4.2.2节)。
而上下文中引起疑惑的主要问题是资源的定义。根据同一份RFC(7231)中,资源的定义如下:
资源: HTTP请求的目标称为“资源”。HTTP不限制资源的性质;它仅定义了一个可能用于与资源交互的接口。每个资源都由统一资源标识符(URI)标识,如[RFC7230]的第2.7节所述。当客户端构造HTTP/1.1请求消息时,它以各种形式之一发送目标URI,如[RFC7230]的第5.3节所定义。当接收到请求时,服务器为目标资源重建一个有效的请求URI(参见[RFC7230]的第5.5节)。HTTP的一个设计目标是将资源标识与请求语义分开,这是通过将请求语义授予请求方法(第4节)和一些修改请求头字段(第5节)来实现的。如果在方法语义和URI本身所暗示的任何语义之间存在冲突(如第4.2.1节所述),则方法语义优先。
因此,在我的理解中,404状态码不应该用于成功的GET请求并返回为空结果的情况(例如:使用特定过滤器的列表未返回任何结果)。

我同意这个评估,但这不是问题的所在。如果你要求一个列表,而它是一个空列表,那么这个空列表仍然存在。问题是,单个项目怎么办? - Evert

3
这些东西可能是主观的,两边都有一些有趣而且不同的论点。然而,在我看来,对于缺失数据返回404状态码是不正确的。这是一个简化的说明以便更清晰地解释:
请求:请给我一些数据吗? 资源(API端点):我会为您获取该请求,并发送数据的潜在响应。
没有出错,端点找到了,表和列也被找到,所以数据库查询并成功返回数据!现在,无论“成功响应”是否有数据都无关紧要,你请求了“潜在”数据的响应,并且获得了“潜在”数据的响应。空值、空白等都是有效数据。
200状态码只是表示我们所做的任何请求都成功了。我请求数据,HTTP/REST 没有出现问题,因为数据(尽管是空的)被返回,所以我的“请求数据”的请求就算是成功的。返回200并让请求方处理空数据,根据每个具体情况进行调整!
考虑以下例子:
请求:使用用户ID 1234查询“违规行为”表 资源(API端点):返回一个响应,但数据为空
数据为空完全是合法的。这意味着该用户没有违规行为。由于这是有效的,因此状态码是200,此时可以这么做:
“您没有违规行为,吃个蓝莓松饼吧!”
如果您认为这是404状态码,那么你在说什么呢?该用户的违规行为无法找到?从语法上讲是正确的,但在REST世界中不正确,因为成功或失败取决于请求。该用户的“违规行为”数据被成功找到,只是没有违规行为——这是一个表示有效状态的真实数字。
以下是一些选择使用哪个状态码时要考虑的事项,无论主观性和棘手的选择:
1. 一致性。 如果您使用404来表示“没有数据”,则每次响应返回空数据时都使用404状态码。 2. 不要使用相同的状态来具有多个含义。如果您返回404表示资源未找到(例如API端点不存在等),则不要将其用于未返回任何数据。这只会使处理响应更加繁琐。 3. 仔细考虑上下文。 请求是什么? 您试图达到什么目标?

3
我认为,使用HTTP响应代码来传输与RESTful服务相关的状态是不合适的。正如@anneb所说,我也认为问题的一部分源于此。任何REST服务对其自身处理的说明都应通过REST特定的代码进行传输。
如果HTTP服务器发现任何已准备好回答其发送的请求的服务,则不应响应HTTP 404——毕竟,服务器找到了某些东西——除非该处理请求的服务明确告知它这样做。
假设以下URL:http://example.com/service/return/test
情况A是服务器“只是在文件系统中查找文件”。如果不存在,则404是正确的。如果它要求某种服务提供此文件并且该服务告诉它没有该名称的内容,则同样适用。
在情况B下,服务器不使用“真实”文件,而实际上请求由其他服务处理,例如某种模板系统。在这种情况下,服务器无法声明资源的存在,因为它对此一无所知(除非由处理它的服务告知)。
如果没有来自服务明确要求不同行为的响应,则HTTP服务器只能说3件事:
  • 如果应该处理请求的服务未运行或未响应,则503
  • 否则为200,因为HTTP服务器实际上可以满足请求-无论服务稍后会说什么;
  • 如果没有找到其他任何内容,则为400404以指示没有这样的服务(与“存在但脱机”相反)。
  • 回到手头的问题:我认为最清晰的方法是根本不使用HTTP响应代码,除了之前提到的。如果服务存在并且正在响应,则HTTP代码应为200。响应应包含服务在单独标头中返回的状态-在这里,服务可以说:
  • REST:EMPTY,例如如果要求其搜索某些内容,并且该研究返回为空;
  • REST:NOT FOUND,如果特别要求sth。“ID-like”-无论是文件名还是资源ID或条目号24等-而该特定资源未找到(通常请求了一个特定资源但未找到);
  • REST:INVALID,如果它发送的任何请求部分未被服务识别。
  • (请注意,我有意为这些加上“REST:”前缀,以标记这样一个事实,即虽然这些可能具有与HTTP响应代码相同的值或措辞,但它们是完全不同的东西)
    让我们回到上面的URL并检查案例B,其中service表示向HTTP服务器指示它本身无法处理此请求,而是将其传递给SERVICE。 HTTP仅提供由SERVICE返回的内容,它不知道任何关于return/test部分的信息,因为这是由SERVICE处理的。如果该服务正在运行,则HTTP应返回200,因为它确实找到了处理该请求的内容。 SERVICE返回的状态(正如上面所述,希望在单独的标头中看到)取决于实际期望采取的操作:
    - 如果return/test要求特定资源:如果存在,则返回具有REST:FOUND状态的资源;如果该资源不存在,则返回REST:NOT FOUND;如果我们知道它曾经存在但不会返回,则可以扩展为返回REST:GONE,如果我们知道它已经离开好莱坞,则可以返回REST:MOVED - 如果将return/test视为搜索或过滤操作:如果结果集为空,请以请求的类型返回一个空集并且状态为REST:EMPTY;以请求的类型返回一组结果并且状态为REST:SUCCESS - 如果return/test不是SERVICE所识别的操作:如果它完全错误(例如像retrun/test这样的拼写错误),则返回REST:ERROR,否则返回REST:NOT IMPLEMENTED 这种区分比混合两种不同的事物要清晰得多。如果有HTTP 404返回,则服务器告诉我:“我不知道你在说什么”。虽然我的请求的REST部分可能完全正常,但我正在所有错误的地方寻找par'Mach。
    另一方面,HTTP 200和REST:ERR告诉我我已经获得服务,但在向服务发送请求时做错了什么。
    从HTTP 200和REST:EMPTY中,我知道我没有做错任何事情-正确的服务器,服务器找到了服务,正确的请求服务-但搜索结果为空。
    问题和讨论出现的原因是HTTP响应代码用于表示由HTTP提供结果的服务的状态,或用于表示不在HTTP服务器本身范围内的内容。由于这种差异,问题无法回答,所有观点都需要进行大量讨论。
    一个服务处理的请求的状态,而不是HTTP服务器,应该遵循不在HTTP响应代码中给出(RFC 6919)。 HTTP代码(RFC 2119)仅应包含来自其自身范围内的信息,即服务是否被发现以处理请求。
    相反,应使用一种不同的方式来告知消费者有关实际处理请求的服务的请求状态。 我的建议是通过特定标头来实现此目的。 理想情况下,标题的名称和内容都遵循标准,使得消费者可以轻松地使用这些响应。

    3
    根据w3,我相信以下内容:
    2xx:
    这类状态码表示客户端的请求已成功接收、理解和接受。
    4xx:
    4xx状态码适用于客户端似乎出错的情况。
    如果客户端请求/users,并且有用户要列出,则响应代码将是200 OK(客户端请求有效)。
    如果客户端请求/users并且没有数据,则响应代码仍为200 OK。 被请求的实体/资源是一个用户列表,该列表存在,只是其中没有任何用户(如果给出空响应,则可以使用204 No Content,但我认为最好使用空列表[])。 客户端请求有效,资源确实存在,因此在这里使用4xx响应代码是不合适的。
    另一方面,如果客户端请求/users/9,并且该用户不存在,则客户端请求了一个不存在的资源,即用户。在这种情况下,回答404 Not Found是有意义的。

    3

    本帖中的答案(截至撰写本文时共有26个)完美地说明了开发人员理解他们正在处理的结构的语义是多么重要。

    如果没有这种理解,可能不会意识到响应状态码是响应的属性,而且只是属性。这些代码存在于响应的上下文中,它们在此上下文之外的含义是未定义的。

    响应本身是请求的结果。请求操作资源。资源、请求、响应和状态码都是HTTP的结构,就HTTP而言:

    HTTP提供了一个统一的接口,用于通过表示的操作和传输(第3节)与资源(第2节)交互,无论其类型、性质或实现方式如何。来源

    换句话说,响应状态码领域受到一个接口的限制,该接口只关心某些目标资源,并处理与这些资源交互使用的消息。服务器应用程序逻辑超出范围,您处理的数据也不重要。

    当使用HTTP时,它总是与资源一起使用。这些资源要么被传输,要么被操作。无论如何,除非我们处于量子世界中,否则资源要么存在,要么不存在,没有第三种状态。
    如果发出HTTP请求以获取(传输)资源的表示形式(就像在这个问题中),而该资源不存在,则响应结果应指示相应的404代码失败。目标-获取表示-未实现,未找到资源。在HTTP上下文中,不应有其他解释结果。
    RFC 7231超文本传输协议(HTTP / 1.1):语义和内容是多次引用的参考资料,但主要作为状态代码描述的参考。我强烈建议阅读整个文档,而不仅仅是第6节,以更好地了解HTTP接口及其组件的范围和语义。

    2

    使用204状态码更为合适。特别是当您有一个警报系统来确保您的网站安全时,404状态码会引起混淆,因为您不知道某些404警报是后端错误还是正常请求但响应为空。


    如果后端出现错误,您不应该返回404。 - appalling22

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接