RESTful的“PUT”操作应该返回什么?

571
我想知道人们对于返回空响应体的RESTful PUT操作有什么看法。
14个回答

769

HTTP规范(RFC 2616)有许多适用的建议。这是我的解释:

  • 对于成功的更新已有资源的PUT请求,HTTP状态码为200 OK。无需响应正文。(根据第9.6节204 No Content更为适用。)
  • 对于成功创建新资源的PUT请求,HTTP状态码为201 Created,Location头字段返回新资源的最具体URI,响应正文中回显其他相关URI和元数据。(RFC 2616第10.2.2节
  • 对于由于第三方修改而失败的PUT请求,HTTP状态码为409 Conflict,响应正文列出尝试更新和当前资源之间的差异。(RFC 2616第10.4.10节
  • 对于不成功的PUT请求,HTTP状态码为400 Bad Request,响应正文用自然语言文本(如英文)解释PUT失败原因。(RFC 2616第10.4节

注意:本答案撰写时RFC 2616是最新的规范,但该RFC已被取代。在引用任何标准时,验证您正在使用的是否为最新版本可能会很有用。


31
有趣!这看起来是Mozilla的臆断,因为我在RFC 2616中找不到任何明确禁止使用 200 作为PUT,DELETE或任何其他方法的规定(特别是10.2成功的2xx10.2.1 200 OK节)。我有什么遗漏吗?比如Mozilla成为W3和IETF的老板? ;) 或者他们可能从未听说过Postel的鲁棒性原则。 - system PAUSE
58
那个句子在2013年2月3日被删除了,可能是因为有人在这里读到了它。;) https://developer.mozilla.org/en-US/docs/HTTP/HTTP_response_codes$compare?from=346227&to=351003 - Christian Strempfer
7
PUT方法的语义是忽略资源当前的状态,因此如果由于第三方修改而导致PUT不成功,则仅在请求是有条件的情况下返回409冲突才有意义。 - Pedro Werneck
13
不返回任何响应正文的成功操作,建议使用204状态码。一些客户端(例如jQuery Ajax)会在期望接收非零长度的响应但未能获得时出错。您可以在此问题中看到这种情况的示例:https://dev59.com/4GEi5IYBdhLWcg3w0O-Q#20929815。 - nick_w
8
可能自此回答以来RFC2616已经更新。在第9.6节中,并没有提到在200状态码下“不需要响应体”。实际上,在PUT请求中并没有提到响应体。它只说明“如果更改了现有资源,则应发送200(OK)或204(No Content)响应代码,以指示请求成功完成。” - james
显示剩余15条评论

225
与大多数答案不同,我认为PUT应该返回更新后的资源(当然还要返回HTTP代码)。
之所以希望在PUT操作中将资源作为响应返回是因为,当您将资源表示发送到服务器时,服务器还可以对此资源进行某些处理,因此客户端想知道请求成功完成后此资源的外观如何(否则它将不得不发出另一个GET请求)。

29
@Raedwald 当然可以。REST并不要求在PUT请求中更新整个资源,尽管这通常是推荐的。有些字段可能没有更新的意义,例如创建日期或上次修改日期,应该不包括在PUT请求体中,但很可能会因为PUT请求而改变。话虽如此,我不同意LiorH认为PUT请求应该返回资源;我会要求在PUT请求后进行GET请求以获取更新后的资源。 - Randolpho
25
“@Randolpho_REST并不要求在PUT操作中更新整个资源”,这是否应该是PATCH操作的情况? - Marco Ciambrone
16
@MarcoCiambrone 是的,我同意并撤回了先前的评论。我已经改变了对REST和PUT的看法 - PUT应始终是幂等的,并且永远不应用于部分更新。 POST是唯一的替代方案,除非支持PATCH,在这种情况下,PATCH可能是一个很好的选择。但是,PATCH是一个新动词,可能不受某些服务器端框架的支持。 - Randolpho
2
答案早在rfc7231之前就已经写好了,但是section 4.3.4明确指出:“PUT方法请求使用请求消息有效负载中封装的表示定义的状态创建或替换目标资源的状态。” - aaaaaa
5
如果更新的对象很大,那该怎么办?返回很多不太可能使用的数据似乎是浪费的。 - beldaz
显示剩余15条评论

9
如果REST API的后端是SQL关系型数据库,那么:
1. 每个可以更新的记录中都应该有“RowVersion”(以避免lost update problem); 2. 在PUT之后,应该始终返回记录的新副本(以获取新的RowVersion)。
如果您不关心丢失的更新,或者想要强制客户端在PUT之后立即执行GET,则不需要从PUT返回任何内容。

8
我认为服务器可以在响应PUT请求时返回内容。如果您使用支持附加数据的响应包装格式(例如Ember-data消耗的格式),则还可以包括通过数据库触发器等方式修改的其他对象。(附加数据明确旨在减少请求的数量,这似乎是优化的好地方。)
如果我只接受PUT而没有任何要报告的内容,则使用状态码204且无主体。如果我有要报告的内容,则使用状态码200,并包括主体。

5

根据传递的请求URI执行状态,Http方法“PUT”可能具有不同的Http状态。下表可以帮助您了解:

enter image description here

4

HTTP/1.1规范(第9.6节)讨论了适当的响应/错误代码。但它并不涉及响应内容。

你期望什么?对我来说,简单的HTTP响应代码(如200等)似乎很直接明了。


是的,但如果您想检查PUT或POST后插入到数据库中的数据是否真正代表您想要的数据,该怎么办呢?如果HTTP能够发送响应正文回来,那就更好了。 - tnkh
2
@tnkh,你提出的建议真是一个可怕的想法。在成功更新后进行单独的GET调用以实现你想要的效果。为了确保性能,在这个部分遇到问题时引入缓存层。我们不能通过玩弄“一切都可以”的逻辑来解决这些问题。不要玩弄“坚实”的和基本的编程原则,这应该是2020年的常识。这是一种耻辱! - XDS
1
@XDS 我承认你评论的第一部分。但是在那之后我忍不住翻白眼。真是个搞笑的评论。 - tnkh
1
感谢您详细解释为什么您觉得这很有趣。 - XDS
1
如果你在这个环节遇到了问题,那么引入缓存层是一个有趣的想法,它展示了为什么可以依赖服务器返回更新后的资源 - 在PUT之后你不能进行缓存,因为你不知道资源应该是什么样子的。如果响应中返回了资源,你可以对其进行缓存,否则你必须执行GET并缓存输出,但减少GET的影响正是你提出引入缓存层的原因。 - kybernetikos

2
我同意上面得票最高的答案,但我想更详细地阐述一下。
REST不是一个标准,因此每个人都可以创建和记录自己的API,只要它符合目的并且有良好的文档、被开发者和使用者所认可和理解。 真正重要的是URL能够识别出暴露的资源。 我已经使用Http Rest API工作多年了,我想分享一下通常我使用的标准方法,这不是因为它完美或者“行为规则”,而是因为我发现它易于使用和向其他人解释。
请注意,我没有提及超媒体,因为这远远超出了答案的范围,但如果感兴趣,可以在OData规范中找到很好的阅读材料。
Create
---------------------------------------------------------------------
Success - 201 Created - Return created object
Failure - 400 Invalid request - Return details about the failure
Async fire and forget operation - 202 Accepted - Optionally return url for polling status

Update
---------------------------------------------------------------------
Success - 200 Ok - Return the updated object
Success - 204 NoContent
Failure - 404 NotFound - The targeted entity identifier does not exist
Failure - 400 Invalid request - Return details about the failure
Async fire and forget operation - 202 Accepted - Optionally return url for polling status

Patch
---------------------------------------------------------------------
Success - 200 Ok - Return the patched object
Success - 204 NoContent
Failure - 404 NotFound - The targeted entity identifier does not exist
Failure - 400 Invalid request - Return details about the failure
Async fire and forget operation - 202 Accepted - Optionally return url for polling status

Delete
---------------------------------------------------------------------
Success - 200 Ok - No content
Success - 200 Ok - When element attempting to be deleted does not exist
Async fire and forget operation - 202 Accepted - Optionally return url for polling status

Get
---------------------------------------------------------------------
Success - 200 Ok - With the list of resulting entities matching the search criteria
Success - 200 Ok - With an empty array

Get specific
---------------------------------------------------------------------
Success - 200 Ok - The entity matching the identifier specified is returned as content
Failure - 404 NotFound - No content

Action
---------------------------------------------------------------------
Success - 200 Ok - Return content where appropriate
Success - 204 NoContent
Failure - 400 - Return details about the failure
Async fire and forget operation - 202 Accepted - Optionally return url for polling status

Generic results
---------------------------------------------------------------------
Authorization error 401 Unauthorized
Authentication error 403 Forbidden
For methods not supported 405
Generic server error 500

0

返回HTTP 201响应代码,表示“已创建”,并附带一个“位置”头,指向客户端可以找到新创建的资源的位置。


8
PUT对象不是(或不应该是)新创建的资源。 - kdazzle
12
PUT方法确实可以创建新的资源,并且通常也是这样做的。请参考http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6。 - Charlie Schliesser
3
只是为了更好地解释我的评论。PUT的意思是将此项放置在特定位置,替换当前位置上的内容(如果适用)。 - user1751825
3
“替换当前内容”是关键词。它应该已经存在并正在被替换。PUT方法不应用于创建新资源。 - Kevin M
4
在最新的RFC文档rfc7231(https://tools.ietf.org/html/rfc7231#section-4.3.4)中,它说资源可以被创建:“PUT方法要求创建或替换目标资源的状态[...]”。你认为PUT不能创建新资源的原因是因为你不一定知道新资源的位置。但是,如果您知道它的位置/标识符,并且该位置尚不存在资源,则可以创建该资源。 - Leo Lei
显示剩余2条评论

0
我在我的服务中使用RESTful API,并且这是我的看法:
首先,我们必须达成共识:使用PUT用于更新资源而不是创建或获取。
我定义了两种资源类型:无状态资源有状态资源
  • 无状态资源 对于这些资源,只需返回带空内容的HttpCode即可。

  • 有状态资源 例如:资源的版本。对于此类资源,您必须在要更改它时提供版本,因此返回完整的资源或将版本返回给客户端,这样在更新操作之后,客户端不需要发送一个get请求。

但是,对于服务或系统,保持简单、清晰、易于使用和维护是最重要的事情。

12
“PUT用于更新资源而非创建或获取” - 这不是真实的也不常见。根据规范,PUT可以创建该资源。清晰明了 = 遵循普遍知道的规范。 - Imre Pühvel

0

RFC7231没有明确规定PUT方法的响应体。你可以按照自己的方式处理。例如:

  • 空(使用状态码No Content以避免一些问题)。
  • 更新后的资源表示。
  • 任何内容。

HTTP并没有准确定义PUT方法对原始服务器状态的影响,除了用户代理请求的意图和原始服务器响应的语义之外。

如果目标资源没有当前表示,并且PUT成功创建了一个表示,则原始服务器必须通过发送201(Created)响应来通知用户代理。

对PUT方法的响应不能被缓存。


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