由于客户LastUser
端LastUpdate
不能修改,因此我会将它们从您的资源表示中完全删除。让我用一个例子来解释我的推理。
假设我们的典型示例 API 将在被要求提供单个资源时向客户端返回以下表示:
GET /example/123
<?xml version="1.0" encoding="UTF-8" ?>
<example>
<id>123</id>
<lorem>ipsum</lorem>
<dolor>sit amet</dolor>
<lastUser uri="/user/321">321</lastUser>
<lastUpdate>2011-04-16 20:00:00 GMT</lastUpdate>
</example>
如果客户端想要修改资源,它可能会获取整个表示并将其发送回 API。
PUT /example/123
<?xml version="1.0" encoding="UTF-8" ?>
<example>
<id>123</id>
<lorem>foobar</lorem>
<dolor>foobaz</dolor>
<lastUser>322</lastUser>
<lastUpdate>2011-04-16 20:46:15 GMT+2</lastUpdate>
</example>
由于 API 为lastUser
和lastUpdate
自动生成值并且不能接受客户端提供的数据,因此最合适的响应是400 Bad Request
or 403 Forbidden
(因为客户端无法修改这些值)。
如果我们想符合 REST 并在执行 PUT 请求时发送资源的完整表示,我们需要从资源的表示中删除lastUser
和lastUpdate
。这将允许客户端通过 PUT 发送完整的实体:
PUT /example/123
<?xml version="1.0" encoding="UTF-8" ?>
<example>
<id>123</id>
<lorem>foobar</lorem>
<dolor>foobaz</dolor>
</example>
现在服务器将接受一个完整的表示,因为它不包含lastUpdate
and lastUser
。
剩下的问题是如何为客户提供访问lastUpdate
和lastUser
. 如果他们不需要它(这些字段只是 API 内部需要的),我们很好,我们的解决方案是完全 RESTful 的。但是,如果客户端需要访问这些数据,最简洁的方法是使用 HTTP 标头:
GET /example/123
...
Last-Modified: Sat, 16 Apr 2011 18:46:15 GMT
X-Last-User: /user/322
...
<?xml version="1.0" encoding="UTF-8" ?>
<example>
<id>123</id>
<lorem>foobar</lorem>
<dolor>foobaz</dolor>
</example>
使用自定义 HTTP 标头并不理想,因为需要教用户代理如何阅读它。如果我们想以更简单的方式为客户提供对相同数据的访问,我们唯一能做的就是将数据放入表示中,我们面临的问题与您最初的问题相同。我至少会尝试以某种方式减轻它。如果 API 使用的内容类型是 XML,我们可以将数据放入节点属性中,而不是直接暴露为节点值,即:
GET /example/123
...
Last-Modified: Sat, 16 Apr 2011 18:46:15 GMT
...
<?xml version="1.0" encoding="UTF-8" ?>
<example last-update="2011-04-16 18:46:15 GMT" last-user="/user/322">
<id>123</id>
<lorem>foobar</lorem>
<dolor>foobaz</dolor>
</example>
这样,我们至少可以避免客户端尝试在后续 PUT 请求中提交所有 XML 节点的问题。这不适用于 JSON,并且该解决方案仍然有点处于幂等性的边缘(因为 API 在处理请求时仍然必须忽略 XML 属性)。
更好的是,正如Jonah在评论中指出的那样,如果客户需要访问lastUser
and lastUpdate
,这些可以作为新资源公开,与原始资源链接,例如如下:
GET /example/123
<?xml version="1.0" encoding="UTF-8" ?>
<example>
<id>123</id>
<lorem>foobar</lorem>
<dolor>foobaz</dolor>
<lastUpdateUri>/example/123/last-update</lastUpdateUri>
</example>
... 进而:
GET /example/123/last-update
<?xml version="1.0" encoding="UTF-8" ?>
<lastUpdate>
<resourceUri>/example/123</resourceUri>
<updatedBy uri="/user/321">321</updatedBy>
<updatedAt>2011-04-16 20:00:00 GMT</updatedAt>
</lastUpdate>
(上面也可以很好地扩展以提供包含单个更改的完整审核日志,前提是资源更改日志可用。)
请注意:
我同意Darrel Miller对这个问题的,但我想在此基础上提供一种不同的方法。请注意,这种方法不受任何标准/RFC/等的支持,它只是对问题的不同看法。