使用 POST 创建请求,响应代码为 200 或 201 和内容

Posted

技术标签:

【中文标题】使用 POST 创建请求,响应代码为 200 或 201 和内容【英文标题】:Create request with POST, which response codes 200 or 201 and content 【发布时间】:2010-12-24 01:21:37 【问题描述】:

假设我编写了一个 REST 服务,其目的是向系统添加新数据项。

我打算发帖到

http://myhost/serviceX/someResources

假设可行,我应该使用什么响应代码?我可以返回什么内容。

我正在查看 HTTP 响应代码的 definitions 并看到这些可能性:

200:返回描述或包含动作结果的实体;

201:表示已创建。含义 *请求已完成并导致创建新资源。新创建的资源可以被响应实体中返回的 URI 引用,资源的最具体的 URI 由 Location 头字段给出。响应应该包括一个实体,其中包含资源特征和位置列表,用户或用户代理可以从中选择最合适的一个。实体格式由 Content-Type 标头字段中给出的媒体类型指定。 *

后者听起来更符合 Http 规范,但我完全不清楚是什么

响应应该包含一个实体 包含资源列表 特征和位置

意思。

建议?解释?

【问题讨论】:

【参考方案1】:

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19

这只是一个冒号分隔的键值。

ETag:“xyzzy”

它可以是任何类型的文本数据——我通常包含一个带有所创建项目标识符的 JSON 字符串。单独测试的简单性使得包含它是值得的。

ETag: " id: 1234, uri: 'http://domain.com/comments/1234', type: 'comment' "

在本例中,所创建项目的标识符、uri 和类型是“资源特征和位置”。

【讨论】:

您是说 ETag 对应于一个包含资源特征和位置列表的实体。我可以看到你的建议很好,非常同意你关于测试的观点。但是,我看不出这与“资源特征和位置列表”有什么关系。 “资源特征和位置列表”将是所提供的任何数据结构的内容。更严格的实现是让 JSON 结构包含资源 uri 以及可能创建的资源类型。我会这样调整答案。 指定问题,以便人们学习。否则,评论只是在挥手。 @SimonGibbs 有什么问题? 虽然按照规范严格正确,但它推荐了一个非常不寻常的实现选项。此外,它实际上并没有回答页面顶部的问题(或者它通过混合单词 ETag 和实体来回答)。 43 票的答案可能更好。【参考方案2】:

我认为atompub REST API 是宁静服务的一个很好的例子。请参阅 atompub 规范中的以下 sn-p:

POST /edit/ HTTP/1.1
Host: example.org
User-Agent: Thingio/1.0
Authorization: Basic ZGFmZnk6c2VjZXJldA==
Content-Type: application/atom+xml;type=entry
Content-Length: nnn
Slug: First Post

<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <title>Atom-Powered Robots Run Amok</title>
  <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
  <updated>2003-12-13T18:30:02Z</updated>
  <author><name>John Doe</name></author>
  <content>Some text.</content>
</entry>

服务器使用状态码 201 表示创建成功。响应包括指示 Atom 条目的成员条目 URI 的 Location 标头,以及响应正文中该条目的表示。

HTTP/1.1 201 Created
Date: Fri, 7 Oct 2005 17:17:11 GMT
Content-Length: nnn
Content-Type: application/atom+xml;type=entry;charset="utf-8"
Location: http://example.org/edit/first-post.atom
ETag: "c180de84f991g8"  

<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <title>Atom-Powered Robots Run Amok</title>
  <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
  <updated>2003-12-13T18:30:02Z</updated>
  <author><name>John Doe</name></author>
  <content>Some text.</content>
  <link rel="edit"
      href="http://example.org/edit/first-post.atom"/>
</entry>

集合创建和返回的条目可能与客户端发布的条目不匹配。服务器可以更改条目中各种元素的值,例如 atom:id、atom:updated 和 atom:author 值,并且可以选择删除或添加其他元素和属性,或者更改元素内容和属性值。

【讨论】:

返回创建的资源可能有点多,如果资源在千兆字节量级... 同意!这是必要性的优化——但你不想过早地这样做。以宁静的精神进行设计并仅在必要时例外处理是很重要的。 @ChandraPatni,Atom 死了。需要更好的例子。 Atom 可能已经死了,但示例的精神仍然存在。 我对 201 响应的原始解释更像是“嘿,你想创建一个资源,但根据上下文,你要么对最终结果不感兴趣,要么有写权限但没有对此资源的读取访问权限。无论哪种情况,在返回主集合之前,您只需要创建资源的 URL。作为创建资源的证据。”基本上,除此之外的任何内容似乎都是 200 响应。除非 RFC 有其他想法。【参考方案3】:

输出实际上取决于所请求的内容类型。但是,您至少应该将创建的资源放在 Location 中。就像 Post-Redirect-Get 模式一样。

在我的情况下,除非另有要求,否则我将其留空。因为这是使用 Response.created() 时 JAX-RS 的行为。

但是,请注意,像 Angular 这样的浏览器和框架不会自动遵循 201。我已经注意到http://www.trajano.net/2013/05/201-created-with-angular-resource/中的行为

【讨论】:

【参考方案4】:

查看HTTP: Method Definitions: POST。

POST 方法执行的操作可能不会产生可以通过 URI 识别的资源。在这种情况下,200(正常)或 204(无内容)是适当的响应状态,具体取决于响应是否包含描述结果的实体。

如果在源服务器上创建了资源,则响应应该是 201(已创建)并包含描述请求状态并引用新资源的实体,以及 Location 标头(参见第 14.30 节)。

【讨论】:

【参考方案5】:

我对此的另一个答案是采取务实的方法并保持您的REST API contract 简单。在我的例子中,我重构了我的 REST API 以使事情更具可测试性,而无需使用 javascript 或 XHR,只需简单的 HTML 表单和链接。

因此,为了更具体地说明您的上述问题,我将使用返回码 200 并让返回的消息包含您的应用程序可以理解的 JSON 消息。根据您的需要,它可能需要新创建的对象的 ID,以便 Web 应用程序可以在另一个调用中获取数据。

请注意,在我重构的 API 合同中,POST 响应不应包含任何可缓存的数据,因为 POST 并不是真正可缓存的,因此将其限制为可以使用 GET 请求请求和缓存的 ID。

【讨论】:

【参考方案6】:

这个想法是响应正文为您提供一个链接您到该事物的页面:

201 创建

201(已创建)状态代码表示请求已完成,并导致创建了一个或多个新资源。请求创建的主要资源由响应中的 Location 标头字段标识,如果未收到 Location 字段,则由有效请求 URI 标识。

这意味着您将在响应header中包含一个Location,它提供了您可以找到新创建的事物的URL >:

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://***.com/a/36373586/12597

响应正文

他们接着提到你应该在回复中包含什么body

201 响应负载通常描述并链接到创建的资源。

对于使用浏览器的人,您可以为他们提供可以查看的内容,然后单击以访问他们新创建的资源:

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://***.com/a/36373586/12597
Content-Type: text/html

Your answer has been saved! 
Click <A href="/a/36373586/12597">here</A> to view it.

如果该页面仅由机器人使用,则将响应设置为计算机可读是有意义的:

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://***.com/a/36373586/12597
Content-Type: application/xml

<createdResources>
   <questionID>1860645</questionID>
   <answerID>36373586</answerID>
   <primary>/a/36373586/12597</primary>
   <additional>
      <resource>http://***.com/questions/1860645/create-request-with-post-which-response-codes-200-or-201-and-content/36373586#36373586</resource>
      <resource>http://***.com/a/1962757/12597</resource>
   </additional>
</createdResource>

或者,如果您愿意:

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://***.com/a/36373586/12597
Content-Type: application/json

 
   "questionID": 1860645, 
   "answerID": 36373586,
   "primary": "/a/36373586/12597",
   "additional": [
      "http://***.com/questions/1860645/create-request-with-post-which-response-codes-200-or-201-and-content/36373586#36373586",
      "http://***.com/a/36373586/12597"
   ]

回复完全取决于您;随心所欲。

缓存友好

最后还有一个优化,我可以预先缓存创建的资源(因为我已经有了内容;我刚刚上传了它)。服务器可以返回一个日期或 ETag,我可以将其与我刚刚上传的内容一起存储:

请参阅Section 7.2,了解201 响应中验证器标头字段(例如ETagLast-Modified)的含义和用途。

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://***.com/a/23704283/12597
Content-Type: text/html
ETag: JF2CA53BOMQGU5LTOQQGC3RAMV4GC3LQNRSS4
Last-Modified: Sat, 02 Apr 2016 12:22:39 GMT 

Your answer has been saved! 
Click <A href="/a/36373586/12597">here</A> to view it.

ETag 是纯粹的任意值。当资源发生变化(并且缓存需要更新)时,让它们有所不同是最重要的。 ETag 通常是一个散列(例如 SHA2-256)。但它可以是数据库rowversion,或递增的修订号。当事物改变时,任何会改变的东西。

【讨论】:

到目前为止,您的反应似乎是最明智的。我有点担心响应的本体,但除此之外,它似乎是对规范的最成熟的解释。我很好奇是否有任何轻量级的“响应式”方式来处理人机输出。但主要是我对你的“缓存你自己的输入”的建议很感兴趣。我知道的大多数网络应用程序都不会创建资源的 1:1 版本。即使它像规范化字符串的大写一样微不足道。将您提交的版本视为创建 etag 的版本是不是有点狡猾? @Anthony,缓存:它可能是一种 1:1 文件存储应用程序。比较例如WebDAV PUT & POST。需要处理的大文件。 @Anthony 是否要将 ETag 返回给客户端取决于您。如果客户端刚刚上传的内容不是你保存的,那么不要返回ETag。这是您的灵活性和您的选择。 为什么您的回复缺少 Content-Length? @VinnieFalco 这是关于 201 响应代码的答案。出于说明目的,已省略 Content-Length。【参考方案7】:

简而言之:

200 创建对象时返回 201 当对象被创建但只返回其引用(例如 ID 或链接)时

【讨论】:

这个来源? 这是我从w3.org/Protocols/rfc2616/rfc2616-sec10.html 和httpstatuses.com/201 了解到的 在阅读了tools.ietf.org/html/rfc7231#section-6.3.1 之后,我同意这种理解——我想我更多的是问你是如何得出这个结论的。但现在在我的理解中...... 200 = 创建并返回的资源 | 201 = 创建资源并返回引用 | 204 = 已创建资源但未返回有效负载 @sudosoul 是否也会像在 201 中一样返回带有 204 的位置标头? @MiguelPynto 根据 RFC 7231,我会说不,位置标头不应返回 204。尽管,204 响应可以包含标头元数据,最终暗示请求成功。检查我发布到 RFC 7231 的链接并查看 204 上的段落。

以上是关于使用 POST 创建请求,响应代码为 200 或 201 和内容的主要内容,如果未能解决你的问题,请参考以下文章

POST 请求的 HTTP 状态码 202 与 200

POST 请求在 Postman 中有效,但在 Python 请求中无效(200 响应机器人检测)

Axios Post 200 响应后不返回数据

在NodeJS中发送不同的POST响应

无法使用赛普拉斯发送 POST 登录请求

HTTP API 设计指南