除了关于REST风格将CRUD映射到HTTP方法的评论之外,这是一个非常好的问题。
你的问题的答案是,是的,在这种情况下你可以自由地使用PUT方法,即使有些资源元素以不幂等的方式被服务器更新。不幸的是,这个答案背后的推理相当模糊。重要的是要理解客户端请求的意图。客户端希望使用传递的值完全替换资源的内容。客户端不负责服务器执行其他操作,因此不会违反HTTP方法的语义。
这就是允许服务器在进行GET操作时更新页面计数器的推理。客户端没有要求更新,因此GET是安全的,即使服务器选择进行更新。
整个完整资源与部分资源的辩论最终已经在更新的HTTP规范中详细说明。
由于可能被解释为部分内容(或者可能是被错误地放置为完整表现的部分内容),源服务器应拒绝包含Content-Range头字段的任何PUT请求。通过定位与重叠该较大资源一部分的状态的另一个标识资源,或使用专门针对部分更新的不同方法(例如[RFC5789]中定义的PATCH方法),可以进行部分内容更新。
所以,我们现在知道应该做什么。不太清楚的是为什么只允许发送完整响应的限制存在。这个问题曾被问过,但我认为在rest-discuss上仍未得到解答。
由于客户端无法修改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>
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>
lastUser
和lastUpdate
的值,无法接受客户端提供的数据。因此,最合适的响应应该是400 Bad Request
或403 Forbidden
(因为客户端无法修改这些值)。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
和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>
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
和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等支持,只是对该问题的不同看法。
example/123/lastUpdate
。 - JonahHTTP方法POST和PUT并不是CRUD的创建和更新的HTTP等效方法。它们都有不同的用途。在某些情况下,使用PUT创建资源或使用POST更新资源是完全可行、有效甚至更好的选择。
当您可以通过特定资源完全更新资源时,请使用PUT。例如,如果您知道一篇文章位于http://example.org/article/1234,您可以通过对此URL进行PUT来直接放置一篇新的资源表示形式的文章。
如果您不知道实际资源位置,例如,当您添加新文章但不知道要存储在哪里时,您可以将其POST到一个URL,并让服务器决定实际的URL。
LastUser
和LastUpdate
- 它们是否是资源表示的一部分(即XML中的节点)? - MicE