例如,您对 users/9
运行 GET 请求,但没有 ID 为 #9 的用户。哪个是最好的响应代码?
200 好
202 接受
204 无内容
400 错误请求
404 未找到
我强烈反对 404 支持 204 或 200 有空数据。或者至少应该使用带有 404 的响应实体。
请求已被接收并正确处理 - 它确实触发了服务器上的应用程序代码,因此不能真正说它是客户端错误,因此整个客户端错误代码 (4xx) 类不合适。
更重要的是,404 可能由于多种技术原因而发生。例如,应用程序在服务器上被暂时停用或卸载,代理连接问题等等。
当然,这种情况下存在 5xx 错误类,但实际上受影响的中间件组件通常无法知道错误在他们这边,然后只是假设错误在客户端,然后以 404 响应而不是 500/503。
因此,仅根据状态代码,客户端无法区分 404 表示“空结果集”和 404 表示“出现严重错误,将此错误报告给运维团队”。
这可能是致命的:想象一下您公司的会计服务列出了所有应获得年度奖金的员工。不幸的是,当它被调用时,它会返回一个 404。这是否意味着没有人应该获得奖金,或者应用程序当前因新部署而关闭,而 404 实际上来自它应该的 tomcat要安装到,而不是从应用程序本身?这两种情况产生相同的状态码,但它们的含义根本不同。
-> 对于需要知道所请求的资源确实不存在而不是暂时无法访问的应用程序,因此没有响应实体的 404 几乎是不可行的。
此外,许多客户端框架通过抛出异常来响应 404,并且不再询问任何问题。这迫使客户端开发人员捕获该异常,对其进行评估,然后基于此决定是否将其记录为由例如监视组件拾取的错误或是否忽略它。这对我来说似乎也不漂亮。
404 相对于 204 的优势在于它可以返回一个响应实体,其中可能包含一些关于为什么找不到所请求的资源的信息。但如果这确实相关,那么人们也可以考虑使用 200 OK 响应并以允许有效负载数据中的错误响应的方式设计系统。或者,可以使用 404 响应的有效负载将结构化信息返回给调用者。如果他收到例如 html 页面而不是他可以解析的 XML 或 JSON,那么这是一个很好的指示,表明出现了技术问题,而不是从调用者的角度来看可能是有效的“无结果”回复。或者可以为此使用 HTTP 响应标头。
不过,我仍然更喜欢带有空响应的 204 或 200。这样,请求的技术执行状态与请求的逻辑结果是分开的。 2xx 表示“技术执行ok,这是结果,处理它”。
我认为在大多数情况下,应该由客户来决定是否可以接受空结果。尽管技术执行正确,但通过在没有响应实体的情况下返回 404,客户端可以决定将案例视为错误,而这些错误根本就不是错误。
另一个简单的类比:“未找到结果”返回 404 就像在 SQL 查询未返回任何结果时抛出 DatabaseConnectionException。它可以完成工作,但是有很多可能的技术原因会引发相同的异常,然后会被误认为是有效的结果。它使用错误机制来传达有效结果。
另一个角度:从操作的角度来看,404 可能是有问题的。由于它可以指示连接问题而不是有效的服务响应,因此我不希望我的指标/仪表板中可能隐藏真正技术问题(例如,请求路由中某处配置错误的代理)的“有效”404 数量波动,这些问题应该进行调查和修复。一些 API 甚至使用 404 而不是 401/403(例如 gitlab 做这样的事情)进一步加剧了这一点,以隐藏请求 URI 将是有效但请求缺乏访问它的授权的信息。在这种情况下,404 也应该被视为技术错误,而不是有效的“找不到资源”结果。
编辑:哇,这引起了很多争议。这是反对 404 的另一个论点:严格从 HTTP 规范 (RFC7231) 的角度来看,404 甚至不意味着资源不存在。这仅意味着服务器没有可用的请求资源的当前表示,这甚至可能只是暂时的。因此,严格按照 HTTP 规范,404 在所请求的事物不存在方面本质上是不可靠的。如果您想传达所请求的事物肯定不存在,请不要使用 404。
TL;DR:使用 404
请参阅This Blog。它很好地解释了它。
博客对 204
的评论摘要:
204 No Content 作为浏览器的响应代码并不是非常有用(尽管根据 HTTP 规范,浏览器确实需要将其理解为“不要更改视图”响应代码)。然而,204 No Content 对于 ajax Web 服务非常有用,它可能希望指示成功而不必返回某些内容。 (特别是在不需要反馈的 DELETE 或 POST 等情况下)。
因此,您的问题的答案是在您的情况下使用 404
。 204
是一个专门的响应代码,您不应经常将其返回到浏览器以响应 GET
。
其他响应代码甚至不如 204
和 404
:
200 应该与您成功获取的任何内容一起返回。当您获取的实体不存在时不合适。 202 用于当服务器已开始处理对象但对象尚未完全准备好时。当然不是这里的情况。您尚未开始,也不会开始构建用户 9 以响应 GET 请求。这打破了各种规则。 400 用于响应格式错误的 HTTP 请求(例如格式错误的 HTTP 标头、错误排序的段等)。这几乎肯定会由您使用的任何框架处理。除非您从头开始编写自己的服务器,否则您不必处理这个问题。编辑:较新的 RFC 现在允许将 400 用于语义上无效的请求。
Wikipedia 的 description of the HTTP status codes 特别有用。您还可以查看定义 in the HTTP/1.1 RFC2616 document at www.w3.org
204
响应代码(无内容)的。
/badurl
或 /user/9
,则这是正确的。开发人员可以通过添加比“未找到”更好的原因短语来提供帮助,但这不是必需的。
The server has not found anything matching the Request-URI
,我倾向于不同意(尽管不反对,请继续阅读)。 Web/应用程序服务器实际上已经找到了一个匹配请求 URI 的动作,尽管 db 服务器没有。但是,它也说 This status code is commonly used when [...] no other response is applicable
,所以我认为这在一定程度上验证了它的用法(即使我不同意/不喜欢它)。
起初,我认为 204 会有意义,但经过讨论,我相信 404 是唯一真正正确的响应。考虑以下数据:
用户:约翰、彼得
METHOD URL STATUS RESPONSE
GET /users 200 [John, Peter]
GET /users/john 200 John
GET /unknown-url-egaer 404 Not Found
GET /users/kyle 404 User Not found
GET /users?name=kyle` 200 []
DELETE /users/john 204 No Content
一些背景:
搜索返回一个数组,它只是没有任何匹配项,但它有内容:一个空数组。 404 当然最出名的是请求的服务器不支持的 url,但缺少的资源实际上是相同的。即使 /users/:name 与 users/kyle 匹配,用户 Kyle 也不是可用资源,因此 404 仍然适用。它不是搜索查询,它是动态 url 的直接引用,所以是 404。根据评论中的建议,自定义 404 的消息是帮助 API 使用者更好地区分完整未知路由和缺失实体的另一种方式。
无论如何,我的两分钱:)
GET /usrs/john 404 Not found
。开发人员无法区分粗手指路线和失踪人员。这就是为什么我推广 GET /users/kyle 404 No such user
和 GET /usrs/john 404 Not found
。
如果预计资源存在,但它可能是空的,我会争辩说,仅获得 200 OK 并带有表示该事物为空的表示可能会更容易。
所以我宁愿让 /things 返回一个带有 {"Items": []} 的 200 OK 而不是一个什么都没有的 204,因为通过这种方式,一个包含 0 个项目的集合可以被视为与一个或里面有更多的项目。
我只是将 204 No Content for PUTs 和 DELETEs 留下,这可能是真的没有有用的表示的情况。
如果 /thing/9 确实不存在,则 404 是合适的。
customers/1/addressbook
)来访问资源,而是调用类似 GetCustomerAddressBook
的端点并接收数据或基本上为空,而不必太担心HTTP。两者都有优点和缺点。
在之前的项目中,我使用过 404。如果没有用户 9,则找不到对象。因此 404 Not Found 是合适的。
对于对象存在,但没有数据,204 No Content 将是合适的。我认为在您的情况下,该对象不存在。
问了两个问题。一个在标题中,一个在示例中。我认为这在一定程度上导致了关于哪种回应是适当的争议。
问题标题询问空数据。空数据仍然是数据,但不等于没有数据。所以这建议请求一个结果集,即一个列表,可能来自 /users
。如果列表为空,它仍然是一个列表,因此 204(无内容)是最合适的。您刚刚要求提供用户列表并提供了一个,它恰好没有内容。
所提供的示例改为询问特定对象、用户 /users/9
。如果未找到用户 #9,则不返回任何用户对象。你请求了一个特定的资源(一个用户对象),但因为没有找到它而没有得到它,所以 404 是合适的。
我认为解决这个问题的方法是,如果您可以在不添加任何条件语句的情况下以您期望的方式使用响应,则使用 204,否则使用 404。
在我的示例中,我可以遍历一个空列表而不检查它是否有内容,但是我不能在不破坏某些内容或添加检查以查看它是否为空的情况下在空对象上显示用户对象数据。
如果适合您的需要,您当然可以使用空对象模式返回一个对象,但这是另一个线程的讨论。
总结或简化,
2xx:可选数据:格式良好的 URI:条件不是 URI 的一部分:如果条件是可选的,可以在 @RequestBody 中指定,@RequestParam 应导致 2xx。示例:按名称/状态过滤
4xx:预期数据:格式不正确 URI:标准是 URI 的一部分:如果标准是强制性的,只能在 @PathVariable 中指定,那么它应该导致 4xx。示例:按唯一 ID 查找。
因此对于所问的情况:“users/9”将是 4xx(可能是 404)但是对于“users?name=superman”应该是 2xx(可能是 204)
根据w3帖子,
200 好
请求已成功。响应返回的信息取决于请求中使用的方法
202 接受
请求已被接受处理,但处理尚未完成。
204 无内容
服务器已完成请求,但不需要返回实体主体,并且可能希望返回更新的元信息。
400 错误请求
由于语法错误,服务器无法理解该请求。客户端不应该在没有修改的情况下重复请求
401未经授权
该请求需要用户身份验证。响应必须包含一个 WWW-Authenticate 头域
404 未找到
服务器未找到任何与请求 URI 匹配的内容。没有说明这种情况是暂时的还是永久性的
204 No Content
.不是 404
,它有一个结果给你,但它没有内容..
现有答案没有详细说明的是,无论您使用路径参数还是查询参数,都会有所不同。
在路径参数的情况下,该参数是资源路径的一部分。在 /users/9 的情况下,响应应该是 404,因为找不到该资源。 /users/9 是资源,结果是一元的,或者错误,不存在。这不是单子。
在查询参数的情况下,该参数不是资源路径的一部分。在 /users?id=9 的情况下,响应应该是 204,因为找到了资源 /users 但它无法返回任何数据。资源 /users 存在且结果为 n 元,即使为空也存在。如果 id 是唯一的,则这是一个 monad。
是使用路径参数还是查询参数取决于用例。我更喜欢用于强制、规范或标识参数的路径参数和用于可选、非规范或属性参数的查询参数(如分页、排序规则语言环境和其他内容)。在 REST API 中,我会使用 /users/9
而不是 /users?id=9
,尤其是因为可能嵌套来获取“子记录”,例如 /users/9/ssh-keys/0
来获取第一个公共 ssh 密钥或 /users/9/address/2
来获取第三个邮政地址。
我更喜欢使用 404。原因如下:
对一元(1 个结果)和 n 元(n 个结果)方法的调用不应无故改变。如果可能的话,我喜欢使用相同的响应代码。预期结果的数量当然是不同的,例如,您希望主体是一个对象(一元)或一个对象数组(n-ary)。
对于 n 元,我会返回一个数组,如果没有结果,我不会返回任何集合(无文档),我会返回一个空集合(空文档,如 JSON 中的空数组或 XML 中的空元素)。也就是说,它仍然是 200,但记录为零。除了身体之外,没有理由将这些信息放在电线上。
204就像一个void方法。我不会将它用于 GET,仅用于 POST、PUT 和 DELETE。如果 GET 标识符是查询参数而不是路径参数,我会例外。
找不到记录就像 NoSuchElementException、ArrayIndexOutOfBoundsException 或类似的东西,由客户端使用不存在的 id 引起,因此,这是客户端错误。
从代码的角度来看,获得 204 意味着代码中可以避免一个额外的分支。它使客户端代码复杂化,在某些情况下,它还使服务器代码复杂化(取决于您使用实体/模型单子还是普通实体/模型;我强烈建议远离实体/模型单子,它可能会导致令人讨厌的错误,因为您认为操作成功并返回 200 或 204 的 monad,而实际上您应该返回其他东西)。
客户端代码更容易编写和理解,如果 2xx 表示服务器完成了客户端请求的操作,而 4xx 表示服务器没有执行客户端请求的操作,这是客户端的错。没有给客户端id请求的记录是客户端的错,因为客户端请求了一个不存在的id。
最后但并非最不重要的一点:一致性
获取/用户/9
PUT /users/9 和 DELETE /users/9
PUT /users/9
和 DELETE /users/9
已经必须返回 204
以防更新或删除成功。那么如果用户 9 不存在,他们应该返回什么?根据所使用的 HTTP 方法,将相同的情况显示为不同的状态代码是没有意义的。
此外,这不是规范,而是文化原因:如果将 204
用于 GET /users/9
,项目中接下来会发生的事情是有人认为返回 204
对 n 元方法有好处。这使客户端代码复杂化,因为客户端现在必须专门检查 204
并在这种情况下跳过解码主体,而不是只检查 2xx
然后解码正文。但是客户会做什么呢?创建一个空数组?那为什么不把它放在电线上呢?如果客户端创建空数组,则 204 是一种愚蠢的压缩形式。如果客户端改用 null
,则会打开一个完全不同的蠕虫罐。
令人遗憾的是,如此简单且定义明确的东西在此线程中变成了“基于意见”。
HTTP 服务器只知道“实体”,它是任何内容的抽象,可以是静态网页、搜索结果列表、其他实体列表、某物的 json 描述、媒体文件等。
每个这样的实体都应该可以通过一个唯一的 URL 来识别,例如
/user/9 -- 单个实体:用户 ID=9
/users -- 单个实体:所有用户的列表
/media/x.mp3 -- 单个实体:名为 x.mp3 的媒体文件
/search -- 单个实体:基于查询参数的动态内容
如果服务器通过给定的 URL 找到一个资源,它的内容是什么并不重要——2G 的数据,null,{},[]——只要它存在,它就会是 200。但如果这样的实体是服务器不知道,预计会返回 404“未找到”。
一个困惑似乎来自开发人员,他们认为如果应用程序具有特定路径形状的处理程序,则它不应该是错误。在 HTTP 协议看来,只要服务器上没有与请求的 URL(请求的 MP3 文件、网页、用户对象等),将返回有效内容(空或其他),它必须是 404(或 410 等)。
另一个混淆点似乎是围绕“没有数据”和“没有实体”。前者是关于一个实体的内容,后者是关于它的存在。
示例 1:
无数据:/users 返回 200 OK,正文:[] 因为还没有人注册
没有实体:/users 返回 404,因为没有路径 /users
示例 2:
无数据:/user/9 返回返回 200 OK,正文:{},因为用户 ID=9 从未输入过他/她的个人数据
没有实体:/user/9 返回 404,因为没有用户 ID=9
示例 3:
无数据:/search?name=Joe 返回 200 OK [],因为数据库中没有 Joe
没有实体:/search?name=Joe 返回 404,因为没有路径 /search
在查看问题后,您不应该使用 404
为什么?
基于 RFC 7231,正确的状态代码是 204
在上面的回答中,我注意到一个小误解:
1.- 资源是:/users
2.- /users/8
不是资源,这是:带有路由参数 8
的资源 /users
,消费者可能无法注意到它并且不知道差异,但发布者知道并且必须知道这一点!...所以他必须为消费者做出准确的回应。时期。
所以:
根据 RFC:404 不正确,因为找到了资源 /users
,但是使用参数 8
执行的逻辑没有找到任何 content
作为响应返回,因此正确答案是:204
这里的重点是:404
甚至没有找到资源来处理内部逻辑
204
是:我找到了资源,执行了逻辑,但我没有使用您在路由参数中给出的条件找到任何数据,因此我无法向您返回任何内容。对不起,请验证您的标准并再次给我打电话。
200
:好的,我找到了资源,执行了逻辑(即使我没有被迫返回任何东西)拿走它并按照你的意愿使用它。
205
:(GET 响应的最佳选项)我找到了资源,逻辑已执行,我有一些内容给你,好好利用它哦,顺便说一下如果你要在视图中分享这个请刷新显示它的视图。
希望能帮助到你。
/users
而不是 /users/8
?那太不正确了。它们都都是资源,它们都由URI(统一的resource标识符)表示。
Twitter 使用带有自定义错误消息的 404,例如“未找到数据”。
参考:https://developer.twitter.com/en/docs/basics/response-codes.html
TL;博士:
如果在 /users/9 中找不到用户,则应返回 404。
如果在 /users?id=9 处未找到用户,则应返回 204。
长版:
在查看了我自己对这些状态代码的使用以及这篇文章中的示例之后,我不得不说,如果在 /users/9
的 url 上未找到用户 #9,则 404 是适当的响应。
在我今天的系统中,我们的 Application Insights 日志充满了数百个已记录的 404 错误,这些错误使我们的日志变得混乱,因为我们决定在 /users/9
没有相关数据时返回 404。然而,这并不意味着我们在设置 responses 时的方法不正确,而是我建议这意味着我们在设置 routing 时的方法不正确。
如果您希望端点获得大量流量并且担心记录太多 404 错误,您应该更改您的路由以符合您想要的状态代码,而不是强制不恰当地使用状态代码。
从那以后,我们决定对我们的代码进行 2 处更改:
通过期望 /users?id=9 更改我们的工作路线 将我们的错误代码更改为 204,这样 404 就不会填满我们的 AI 日志。
归根结底,API 的架构师需要了解他们的 API 将如何使用以及哪种路由适合该用例。
我相信在 /users/9
的情况下,您请求的资源是用户本身,用户 #9;您要求服务器使用标识为“9”的对象进行响应,该对象恰好存在于其中包含“用户”一词的路径中。如果找不到该对象,您应该得到 404。
但是,如果您调用 /users?id=9
,我觉得您请求的资源是用户控制器,同时还提供了更多的特异性,因此它不会返回所有用户的完整列表。您要求服务器响应特定用户,该用户可以通过查询字符串中定义的 ID 号来识别。因此,如果没有找到数据,我认为 204 将适用,因为即使没有找到数据,控制器也是如此。
查询字符串方法还完成了一些我认为不仅对 API 开发人员而且对客户端开发人员(尤其是初级开发人员或继承此代码或对其进行调用的代码的未来开发人员)都有帮助的事情:
任何参与其中的人都会立即清楚 9 是一个 ID,而不是某个任意数字。在这样一个基本示例中,这一点似乎没有意义,但考虑一个使用 GUID 作为行 ID 或允许您通过人名获取数据的系统,或者甚至是一个返回特定邮政编码而不是行 ID 信息的系统.如果所有相关的开发人员一眼就能知道该识别参数是名字、姓氏、全名,还是邮政编码而不是 ID,这对所有相关的开发人员都是有用的。
我想说,两者都不是很合适。正如@anneb 所说的那样,我也认为部分问题来自使用HTTP 响应代码来传输与RESTful 服务相关的状态。 REST 服务必须说明的任何关于其自身处理的内容都应通过 REST 特定代码进行传输。
1
我认为,如果 HTTP 服务器发现任何准备好响应它发送的请求的服务,它不应该以 HTTP 404 响应——最终,服务器找到了一些东西——除非由服务器明确告知处理请求的服务。
让我们暂时假设以下 URL:http://example.com/service/return/test
。
情况 A 是服务器“只是在文件系统上查找文件”。如果不存在,则 404 是正确的。如果它要求某种服务准确地传递此文件并且该服务告诉它不存在该名称的任何内容,则也是如此。
在情况 B 中,服务器不使用“真实”文件,但实际上请求由其他服务处理 - 例如某种模板系统。在这里,服务器不能对资源的存在做出任何声明,因为它对它一无所知(除非处理它的服务告诉它)。
如果服务没有任何明确要求不同行为的响应,HTTP 服务器只能说 3 件事:
503 如果应该处理请求的服务没有运行或没有响应;
200 否则,因为 HTTP 服务器实际上可以满足请求 - 无论服务稍后会说什么;
400 或 404 表示没有这样的服务(与“存在但离线”相反)并且没有找到其他任何服务。
2
回到手头的问题:我认为最干净的方法是不使用 HTTP 任何响应代码,而不是之前说的。如果服务存在并响应,则 HTTP 代码应为 200。响应应包含服务返回的状态在单独的标头中 - 在这里,服务可以说
REST:EMPTY 例如,如果它被要求搜索某事。而那项研究却空无一物;
REST:NOT FOUND 如果它是专门为某事而要求的。 “ID-like”——是一个文件名或一个 ID 或条目号 24 等的资源——并且未找到该特定资源(通常,请求了一个特定资源但未找到);
REST:INVALID 如果发送的请求的任何部分未被服务识别。
(请注意,我在它们前面加上“REST:”是为了标记这样一个事实,即虽然它们可能与 HTTP 响应代码具有相同的值或措辞,但它们是完全不同的)
3
让我们回到上面的 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 识别的操作:如果完全错误,则返回 REST:ERROR(例如,像 rerun/test 这样的错字),或者 REST:NOT IMPLEMENTED 以防以后计划。
4
这种区别比将两种不同的东西混合起来要干净得多。如果有的话,它还将使调试更容易,处理也只会稍微复杂一些。
如果返回 HTTP 404,服务器会告诉我,“我不知道你在说什么”。虽然我的请求的 REST 部分可能完全没问题,但我在所有错误的地方寻找 par'Mach。
另一方面,HTTP 200 和 REST:ERR 告诉我我得到了服务,但是在我对服务的请求中做错了。
从 HTTP 200 和 REST:EMPTY,我知道我没有做错任何事情——正确的服务器,服务器找到了服务,正确的请求服务——但是搜索结果是空的。
概括
问题和讨论源于这样一个事实,即 HTTP 响应代码被用来表示服务的状态,其结果由 HTTP 提供服务,或者表示某事。这不在 HTTP 服务器本身的范围内。由于这种差异,无法回答问题,所有意见都需要进行大量讨论。
由服务而不是 HTTP 服务器处理的请求的状态真的不应该(RFC 6919)由 HTTP 响应代码给出。 HTTP 代码应该 (RFC 2119) 仅包含 HTTP 服务器可以从其自身范围内提供的信息:即,是否找到服务来处理请求。
相反,应该使用一种不同的方式来告诉消费者关于实际处理请求的服务的请求状态。我的建议是通过一个特定的标题来做到这一点。理想情况下,标头的名称及其内容都遵循使消费者可以轻松处理这些响应的标准。
根据 RFC7231 - page59(https://www.rfc-editor.org/rfc/rfc7231#page-59) 404 状态码响应的定义是:
6.5.4. 404 Not Found 404(未找到)状态码表示源服务器没有找到目标资源的当前表示或不愿意透露存在的表示。 404 状态码并不表示这种缺乏代表性是暂时的还是永久性的;如果源服务器知道(大概是通过一些可配置的方式)该条件可能是永久性的,则 410(Gone)状态代码优于 404。默认情况下,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 节所述,则方法语义优先。
因此,在我的理解中,不应在成功的 GET 请求中使用 404 状态代码,但结果为空。(例如:特定过滤器没有结果的列表)
这样的事情可能是主观的,双方都有一些有趣的和各种可靠的论据。但是[在我看来]为丢失的数据返回 404 是不正确的。为了清楚起见,这里有一个简化的描述:
请求:请问我可以提供一些数据吗?
资源(API 端点):我会在此处为您获取该请求 [发送潜在数据的响应]
没有任何问题,找到了端点,找到了表和列,因此查询数据库并“成功”返回数据!
现在-“成功的响应”是否有数据并不重要,您要求对“潜在”数据的响应,并且具有“潜在”数据的响应已得到满足。 Null、empty 等是有效数据。
200 只是意味着我们所做的任何请求都是成功的。我正在请求数据,HTTP/REST 没有出现任何问题,并且由于返回了数据(尽管为空),我的“数据请求”成功。
返回 200 并让请求者处理每个特定场景需要的空数据!
考虑这个例子:
请求:查询用户 ID 为 1234 的“违规”表
资源(API 端点):返回响应但数据为空
该数据为空是完全有效的。这意味着用户没有违规行为。这是一个 200,因为它都是有效的,然后我可以这样做:
你没有违规,吃蓝莓松饼!
如果您认为这是 404,您在说明什么?找不到用户的违规行为?现在,在语法上这是正确的,但在 REST 世界中它是不正确的,成功或失败与请求有关。可以成功找到该用户的“违规”数据,违规次数为零 - 一个实数表示有效状态。
[厚颜无耻的笔记..]
在您的标题中,您下意识地同意 200 是正确的回答:
有效请求但空数据的正确 REST 响应代码是什么?
无论主观性和棘手的选择如何,在选择要使用的状态码时,都需要考虑以下几点:
一致性。如果您将 404 用于“无数据”,则每次响应均未返回数据时使用它。不要将同一状态用于多个含义。如果您在未找到资源时返回 404(例如 API 端点不存在等),那么也不要将它用于没有返回数据。这只会让处理响应变得很痛苦。仔细考虑上下文。什么是“要求”?你说你想要达到什么目的?
根据 Microsoft 的说法:Controller action return types in ASP.NET Core web API,向下滚动几乎到底部,您会发现以下关于 404 的简介,与数据库中未找到的对象有关。在这里,他们建议 404 适用于空数据。
https://i.stack.imgur.com/4WSCO.png
该线程中的答案(在撰写本文时为 26 个)完美地说明了开发人员理解他们正在使用的构造的语义是多么重要。
如果没有这种理解,响应状态代码可能并不明显是响应的属性而不是其他属性。这些代码存在于响应的上下文中,并且它们在此上下文之外的含义是未定义的。
响应本身就是请求的结果。该请求对资源进行操作。资源、请求、响应和状态码是 HTTP 的构造,就 HTTP 而言:
HTTP 提供了一个统一的接口来与资源进行交互(第 2 节),无论其类型、性质或实现如何,都通过表示的操纵和传输(第 3 节)。资源。
换句话说,响应状态码的范围受限于只关心一些目标资源的接口,并处理用于与这些资源交互的消息。服务器应用程序逻辑超出范围,您使用的数据也不重要。
当使用 HTTP 时,它总是与资源一起使用。资源被以太转移或操纵。无论如何,除非我们处于量子世界中,否则资源要么存在,要么不存在,不存在第三态。
如果发出 HTTP 请求以获取(传输)资源的表示(如本问题所示)并且该资源不存在,则响应结果应指示失败,并带有相应的 404 代码。未达到目标 - 获取表示 - 未找到资源。在 HTTP 的上下文中不应该有对结果的其他解释。
RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content,此处多次提及,但主要是作为状态码描述的参考。我强烈建议通读整个文档,而不仅仅是第 6 节,以更好地理解 HTTP 接口及其组件的范围和语义。
204更合适。特别是当您有一个警报系统来确保您的网站是安全的时,在这种情况下 404 会引起混淆,因为您不知道某些 404 警报是后端错误或正常请求但响应为空。
正如许多人所说,404 具有误导性,它不允许客户端区分请求 uri 是否不存在,或者请求的 uri 是否无法获取请求的资源。
预计 200 状态将包含资源数据 - 因此它不是正确的选择。 204 状态完全意味着其他东西,不应用作 GET 请求的响应。
由于某种原因,所有其他现有状态均不适用。
我已经看到这个话题在多个地方被反复讨论。对我来说,很明显,要消除围绕该主题的混淆,需要一个专门的成功状态。类似“209 - 没有资源可显示”。
这将是 2xx 状态,因为找不到 ID 不应被视为客户端错误(如果客户端知道服务器数据库中的所有内容,他们就不需要向服务器询问任何内容,不是吗?)。此专用状态将解决与使用其他状态进行辩论的所有问题。
唯一的问题是:我如何让 RFC 接受它作为标准?
只是一个在这种情况下多次挣扎的开发人员的补充。您可能已经注意到,当特定资源不存在时,是否返回 404 或 200 或 204 始终是一个讨论。上面的讨论表明,这个主题非常令人困惑并且基于意见(虽然存在一个 http-status-code 标准)。我个人建议,因为我猜它还没有被提及,无论你如何决定在你的 API 定义中记录它。当然,当客户端开发人员使用您的特定“REST”-api 来使用他/她关于 Rest 的知识并期望您的 api 以这种方式工作时,他/她会想到。我猜你看到了陷阱。因此,我使用了一个自述文件,其中我明确定义了在哪些情况下我使用哪些状态码。这并不意味着我使用一些随机定义。我总是尝试使用标准,但为了避免这种情况,我记录了我的用法。客户可能会认为您在某些特定情况下是错误的,但正如文档所记录的那样,没有必要进行额外的讨论,这可以为您和开发人员节省时间。
对 Ops 问题的一句话:当我回想起开始开发后端应用程序并且我在控制器路由中配置了一些错误以便不调用我的控制器方法时,我总是会想到 404 代码。考虑到这一点,我认为如果请求确实在 Controller 方法中到达您的代码,则客户端执行了有效请求并且找到了请求端点。所以这是不使用 404 的指示。如果 db 查询返回未找到,我返回 200 但正文为空。
根据 w3,我相信以下几点:
2xx:
此类状态码表示客户端的请求被成功接收、理解和接受。
4xx:
4xx 类状态码适用于客户端似乎出错的情况。
如果客户端请求 /users
,并且它有要列出的用户,则响应代码将为 200 OK
(客户端请求有效)。
如果客户端请求 /users
并且它没有数据,则响应代码仍然是 200 OK
。
请求的实体/资源是 用户列表,该列表存在,只是没有任何用户(如果给出空响应,可以使用 204 No Content
,尽管我认为空列表 []
会更好)。
客户端请求有效,并且资源确实存在,所以 4xx 响应代码在这里没有意义。
另一方面,如果客户端请求 /users/9
,而该用户不存在,则客户端通过请求不存在的资源(用户)而犯了错误。在这种情况下,用 404 Not Found
回答是有意义的。
使用允许客户端打开它并相应地分叉逻辑的通用枚举对响应内容进行编码。我不确定您的客户如何区分“未找到数据”404 和“未找到网络资源”404?您不希望有人浏览到 userZ/9 并让客户端感到疑惑,就好像请求有效但没有返回数据一样。
如果您仅仅因为没有响应数据而返回,那么 404 对任何客户端来说都会非常混乱。
对我来说,带有空主体的响应代码 200 足以理解一切都是完美的,但没有符合要求的数据。
我不认为 404 是正确的响应。
如果你使用404,你怎么知道是没有找到api或者你的数据库中的记录没有找到?
根据您的描述,我会使用 200 OK,因为您的 api 执行了所有逻辑而没有任何问题。它只是在数据库中找不到记录。所以,这不是 API 问题,也不是数据库问题,这是你的问题,你认为记录存在但它不存在。因此,API 执行成功,数据库查询执行成功,但没有找到任何返回。
因此,在这种情况下,我会使用
200 好
使用空响应,例如数组的 [] 或对象的 {}。
{id, model, year}
以使其简单,因此只是一个具有简单字段的 JSON 对象。如果汽车 1 不存在,您是否介意澄清如果我发出 GET: /uri/to/cars/carid (/uri/to/cars/1) 我应该得到什么?我的意思是我应该按照适当的做法恢复状态和身体?非常感激
404
,也许是一个更专业的 410
,对于正文,我建议使用常见的错误格式,例如 RFC 7807。
我们在这里看到的是两种范式之间的张力。
在 HTTP 和 REST 中,URL 标识一个资源,user/9
资源包括 9。因此 HTTP 应该返回 404 - 未找到。这就是 RESTful 的定义。稍后,您可以 PUT user/9
,然后当您下次获取时,您将获取数据。这就是 HTTP 和 REST 的设计方式。
在 HTTP 级别,如果您不想要 404,更好的 URL 将是 user?id=9
,然后会找到 user
部分,并且该函数可以进行自己的处理并返回它自己的通知方式“不成立”。
但是,为了方便指定 API,使用 user/9
格式“更好”。这给我们留下了一个两难的境地:这个请求是通过 HTTP 发出的,而(自认为的)正确的 HTTP 答案是 404;但从 API 消费者的角度来看,框架可能无法很好地处理 404,他们想要一个 200 + 一个“未找到”的有效负载(204 对许多框架来说也可能有问题)。
这种在已经定义的协议 (HTTP) 之上分层的 API 是造成紧张的原因。当设计为真正的 RESTful API(并正确处理会产生的 404)时,没有问题。
如果您认为 user/9
应该返回 200 +“未找到”,那么您使用 user
作为 RPC 端点,然后在 URL 的其余部分编码参数。我认为这是一个糟糕的设计,与 RESTful 规范相反,并且完全理解我们是如何到达这里的。
如果你控制两端 - 考虑到你的服务器的限制 - 更有可能 - 你的客户端框架,做什么工作。
(想想在 user/9
上执行 HEAD 请求的后果 - 您无法在响应中提供任何内容。200 表示 user/9
确实存在,而 404 (正确)表示它不存在.)
为什么不使用410?它表明请求的资源不再存在,并且在您的情况 users/9
中,客户端应该永远不会请求该资源。
您可以在此处找到有关 410 的更多详细信息:https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
在这种情况下,Ruby on Rails
以 404 Not Found
响应。
客户端请求的资源不存在。因此,404 Not Found
更合适。
编辑
我看到,在这种情况下,许多开发人员不喜欢 404 not found
。
如果您不想使用 404
,我认为,您可以使用以下两个响应代码中的任何一个:
200 好
204 无内容
如果您使用 200 OK
: 响应正文应为 empty json
:[]
或 {}
如果您使用 204 OK
: 响应正文应为空。
503 Service Unavailable
。/users
路由,而是/users/9
,即“称为 #9 的用户”),因此您的“空结果集”比较没有意义. 404 表示该对象不存在。