假设我编写了一个 REST 服务,其目的是向系统添加一个新数据项。
我打算发布到
http://myhost/serviceX/someResources
假设可行,我应该使用什么响应代码?我可以返回什么内容。
我正在查看 HTTP 响应代码的 definitions 并看到以下可能性:
200:返回描述或包含动作结果的实体;
201:表示已创建。含义 *请求已完成并导致创建新资源。新创建的资源可以被响应实体中返回的 URI 引用,资源的最具体的 URI 由 Location 头字段给出。响应应该包含一个实体,其中包含资源特征和位置列表,用户或用户代理可以从中选择最合适的一个。实体格式由 Content-Type 标头字段中给出的媒体类型指定。 *
后者听起来更符合 Http 规范,但我完全不清楚是什么
响应应该包括一个包含资源特征和位置列表的实体
方法。
建议?解释?
这个想法是响应正文为您提供了一个将您链接到该事物的页面:
201 已创建 201(已创建)状态代码表示请求已完成并导致创建了一个或多个新资源。请求创建的主要资源由响应中的 Location 头字段标识,如果没有收到 Location 字段,则由有效的请求 URI 标识。
这意味着您将在响应 header 中包含一个 Location
,它提供了您可以找到新创建的 thing 的 URL:
HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/36373586/12597
响应正文
然后他们继续提到您应该在响应正文中包含的内容:
201 响应负载通常描述并链接到创建的资源。
对于使用浏览器的人,您可以为他们提供可以查看的内容,然后单击以访问他们新创建的资源:
HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.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://stackoverflow.com/a/36373586/12597
Content-Type: application/xml
<createdResources>
<questionID>1860645</questionID>
<answerID>36373586</answerID>
<primary>/a/36373586/12597</primary>
<additional>
<resource>http://stackoverflow.com/questions/1860645/create-request-with-post-which-response-codes-200-or-201-and-content/36373586#36373586</resource>
<resource>http://stackoverflow.com/a/1962757/12597</resource>
</additional>
</createdResource>
或者,如果您愿意:
HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/36373586/12597
Content-Type: application/json
{
"questionID": 1860645,
"answerID": 36373586,
"primary": "/a/36373586/12597",
"additional": [
"http://stackoverflow.com/questions/1860645/create-request-with-post-which-response-codes-200-or-201-and-content/36373586#36373586",
"http://stackoverflow.com/a/36373586/12597"
]
}
回应完全取决于您;随心所欲,随心所欲。
缓存友好
最后是我可以预先缓存创建的资源的优化(因为我已经有了内容;我刚刚上传了它)。服务器可以返回一个日期或 ETag
,我可以将其与我刚刚上传的内容一起存储:
有关 201 响应中验证器标头字段(例如 ETag 和 Last-Modified)的含义和用途的讨论,请参见第 7.2 节。
HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.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
或递增的修订号。当 事物 改变时会 改变 的任何事物。
简单来说:
创建并返回对象时为 200
201 创建对象但仅返回其引用(例如 ID 或链接)
我认为 atompub REST API 是宁静服务的一个很好的例子。请参阅 atompub 规范中的以下片段:
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 值,并且可以选择删除或添加其他元素和属性,或者更改元素内容和属性值。
查看HTTP: Method Definitions: POST。
POST 方法执行的操作可能不会产生可由 URI 标识的资源。在这种情况下,200(正常)或 204(无内容)是适当的响应状态,具体取决于响应是否包含描述结果的实体。如果在源服务器上创建了资源,则响应应该是 201(已创建)并包含描述请求状态并引用新资源的实体和 Location 标头(参见第 14.30 节)。
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和类型是“资源特征和位置”。
输出实际上取决于所请求的内容类型。但是,您至少应该将创建的资源放在 Location 中。就像 Post-Redirect-Get 模式一样。
就我而言,除非另有要求,否则我将其留空。因为这是使用 Response.created() 时 JAX-RS 的行为。
但是,请注意,像 Angular 这样的浏览器和框架不会自动遵循 201。我注意到 http://www.trajano.net/2013/05/201-created-with-angular-resource/ 中的行为
我对此的另一个答案是采取务实的方法并保持您的 REST API contract 简单。在我的例子中,我重构了我的 REST API 以使事情更易于测试,而无需求助于 JavaScript 或 XHR,只需简单的 HTML 表单和链接。
因此,为了更具体地说明您的上述问题,我只使用返回码 200
并让返回的消息包含您的应用程序可以理解的 JSON 消息。根据您的需要,它可能需要新创建的对象的 ID,以便 Web 应用程序可以在另一个调用中获取数据。
请注意,在我重构的 API 合同中,POST 响应不应包含任何可缓存的数据,因为 POST 并不是真正可缓存的,因此将其限制为可以使用 GET 请求请求和缓存的 ID。