如何在多个项目中使用ETag / If-Match?

23

在RESTful接口中处理乐观锁的推荐方式似乎是从GET返回ETag,并在PUT上提供一个If-Match,即:

GET /items/1  --> gives client an ETag for a single item
PUT /items/1  <-- client gives it back as If-Match for checking

如果我想要批量从一个URI中GET多个项,我该如何使用这个方案?

GET /items    --> How do I return multiple ETags for multiple items here? 

另外,如果ETags / If-Match无法应对这种情况,有什么推荐的方法吗?还是我应该自己动手?


你是如何在第一时间使用单个URL检索多个项目的? - Remy Lebeau
查看第二个示例URI... GET /items 将返回一个项目列表。 - stusmith
什么类型的列表?你能展示实际数据吗? - Remy Lebeau
1
这一切仍处于规划阶段。我的问题是:ETag 信息是在 HTTP 标头中返回的,那么我如何为多个项目返回适当的信息? - stusmith
1
您无法在标头中返回多个ETag。根据正文数据的格式(XML等),您可以将ETag作为项目本身的属性/节点包含在其中。这就是我问列表格式的原因。 - Remy Lebeau
2个回答

27

简而言之: 给包含“集合”的资源分配ETag是可以的,但确定有效ETag时需要应用程序管理该集合中项目的标识/更改。

在REST架构风格中,资源并不意味着它是单个项或集合。它仅表示工作流程中给定点上应用程序的状态。资源将包含描述性数据,并希望在工作流程的那一点上提供可能的下一步链接。工作流程可能非常简单,例如获取订单状态,也可能非常复杂,例如在电子商务网站上订购商品,并具有取消订单的功能。

etag是一种用于确定特定状态(资源)是否最新的机制。将ETag分配给返回数据集合的资源意味着您的应用程序正在识别该点上集合项目的状态,如果这些项目中的任何内容发生更改,则先前的ETag值将不再有效。由您的应用程序决定何时更改集合项以及如何识别各个项。换句话说,ETag适用于资源,但不适用于给定资源的单个组件。


3
我发现另一种方法是使用返回内容的哈希值。在这种情况下,哈希将在集合上计算,因此可以作为ETag标头的候选项。但是,如何将此ETag用于乐观锁定呢?例如,客户端可能决定更新集合中的某个项目,但他没有此特定项目的哈希值。 - manash
1
不确定这是否正确,但我会将哈希作为集合中每个实体的一部分进行回退。我个人想不到其他办法。或者在准备好更新时让客户端提取他们想要更新的单个实体。 - gdp
2
有时候你可能需要与API多交流一些。如果你要对集合中的某个项执行更新操作,请对其资源URI执行头请求,并使用该Etag执行乐观锁定。 - The Prophet
1
eTag并不是这样使用的。eTag标头的值只能在服务器端生成。要查看准确的定义,请参阅HTTP标准的第14.19节。REST一词在常用语中被视为HTTP的同义词,但它们实际上是非常不同的。 - Sixto Saez
1
REST和HTTP是非常不同的概念。不幸的是,REST === HTTP的命名战已经输掉了。尤其是因为数据库并发的概念都不适用于这两个术语,理解它们的区别可能会很有帮助。 - Sixto Saez
显示剩余4条评论

0
在聚合的情况下,聚合的版本必须是最后一个更新部分的版本。使用事件溯源,这很简单,因为您可以使用事件 ID 进行版本控制。
至于您的项目集合,集合资源的 etag 必须是最后一个更新的项目资源的 etag。对于每个项目,您必须在其超链接中描述 etag,或者如果所有超链接在同一资源中具有相同的版本,则可以在包含资源中描述版本。
例如,如果项目 4 是最新更新的,则 `GET /items` 应该返回类似以下内容:
{
    "type": "/doc/ItemSet",
    "version": "3q2teg3234",
    "label": "The Collection of Expensive Items",
    "size": 2,
    "items": [
        {
            "type": "/doc/Item",
            "version": "f233425wfsw",
            "id": "1",
            "label": "Bugatti Veyron",
            "links": [
                {
                    "label": "Read item",
                    "type": "/doc/Item/methods/read"
                },
                {
                    "label": "Update item",
                    "type": "/doc/Item/methods/update"
                }
            ]
        },
        {
            "type": "/doc/Item",
            "version": "3q2teg3234",
            "label": "Porsche 911",
            "id": "4",
            "links": [
                {
                    "label": "Read item",
                    "type": "/doc/Item/methods/read"
                },
                {
                    "label": "Update item",
                    "type": "/doc/Item/methods/update"
                }
            ]
        },
    ],
    "links": [
        {
            "label": "List items",
            "type": "/doc/ItemSet/methods/list"
        }
    ]
}

当然,您可以在同一文档中发送超链接描述,例如/doc/Item/methods/update,但最好有可重用的内容,例如:

{
    "type": "/doc/HyperLinkTemplate",
    "method": "PUT",
    "uri": "/api/items/{id}",
    "headers": {
        "ETag": "{version}"
    },
    "body": {
        "label": "{label}"
    },
    "parameters": {
        "version": {
            "type": "/doc/ResourceVersion",
            "value": {
                "type": "doc/Query",
                "query": "{context/containingResource/version}"
            },
            "writeable": false
        },
        "id": {
            "type": "/doc/Item/id",
            "value": {
                "type": "doc/Query",
                "query": "{context/containingResource/id}"
            },
            "writeable": false
        },
        "label": {
            "type": "/doc/Item/label",
            "value": {
                "type": "doc/Query",
                "query": "{context/containingResource/label}"
            },
            "writeable": true
        }
    }
}

如何使用超链接取决于您的客户端。甚至可以从中生成HTML表单,但REST主要用于M2M通信。


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