【问题标题】:ETags and collectionsETags 和集合
【发布时间】:2023-03-31 17:05:01
【问题描述】:

许多 REST API 提供了搜索资源的能力。

例如,可以使用以下 HTTP 请求获取类型 A 的资源:

GET /A?prop1={value1}&prop2={value2}

我正在使用乐观锁定,因此希望为每个返回的 A 类型资源返回一个版本。到目前为止,我在使用其 ID 仅获取一个资源时使用 ETag 标头。

是否有一种 HTTP 方法可以在同一响应中返回多个资源的版本?如果不是,我应该在正文中包含版本吗?

谢谢, 迈克尔


编辑:我在网上发现 ETag 通常是通过计算部分回复的哈希值生成的。这种方法非常适合我的情况,因为将计算返回集合的哈希值。但是,如果客户端决定更新集合中的某个元素,他应该将哪个 ETag 放入 If-Match 标头中?我认为包含单个元素的 ETag 是唯一的解决方案...

【问题讨论】:

  • 什么是 API 中的“多资源”?
  • A 类型资源的集合。但是,该集合本身并不是资源。它包含彼此独立的资源。因此,这些资源中的每一个都有自己的版本。
  • 如果我的理解是正确的,在多个资源的情况下,您的响应不会有 ETag ,而是每个资源的版本将是响应正文的一部分,每个资源的 HTTP PUT 请求将在“if-modified-since”标头中包含版本信息。对吗?

标签: rest http etag optimistic-locking


【解决方案1】:

我认为这在一定程度上取决于减少带宽的资源、数据和请求的数量。但一个解决方案可能是在子请求中分离资源。

假设GET /images?car=mustang&viewangle=front的群呼返回5张图片。现在您可以将所有图像包含为二进制数据,并且 GET 请求本身具有唯一的 ETag:

GET /images?car=mustang&viewangle=front
...
HTTP 1.1 200 OK
ETag "aaaaaa"

data:image/png;base64,a123456....
data:image/png;base64,b123456....
data:image/png;base64,c123456....
data:image/png;base64,d123456....
data:image/png;base64,e123456....

现在的问题是,添加的一张图片改变了群呼的 ETag,您需要再次传输完整的集合,尽管只有一张图片发生了变化:

GET /images?car=mustang&viewangle=front
If-None-Match "aaaaaa"
...
HTTP 1.1 200 OK
ETag "bbbbbb"

data:image/png;base64,a123456....
data:image/png;base64,b123456....
data:image/png;base64,c123456....
data:image/png;base64,d123456....
data:image/png;base64,e123456....
data:image/png;base64,f123456....

在这种情况下,最好的解决方案是将资源数据与群组通话分开。所以响应只包含子请求的信息:

GET /images?car=mustang&viewangle=front
...
HTTP 1.1 200 OK
ETag "aaaaaa"

a.jpg
b.jpg
c.jpg
d.jpg
e.jpg

这样每个子请求都可以单独缓存:

GET /image/?src=a.jpg
If-None-Match "Akj5odjr"
...
HTTP 1.1 304 Not Modified

统计数据
- 第一个请求 = 6x 200 OK
- 如果组未更改,则未来的请求 = 1x 304 Not Modified
- 如果添加了一个新资源,则未来的请求 = 2x 200 OK, 5x 304 Not Modified

现在我将调整 API 文档。这意味着请求者必须在调用之前检查子请求的缓存是否可用。这可以通过在组请求中提供 ETag(或其他哈希)来完成:

GET /images?car=mustang&viewangle=front
...
HTTP 1.1 200 OK
...
ETag "aaaaaa"

a.jpg;AfewrKJD
b.jpg;Bgnweidk
c.jpg;Ckirewof
d.jpg;Dt34gsd0
e.jpg;Egk29dds
f.jpg;F498wdn4

现在请求者检查缓存并发现a.jpg 有一个名为Akj5odjr 的新ETag,f.jpg;F498wdn4 是一个新条目。这样可以减少未来的请求:

统计数据
- 第一个请求 = 6x 200 OK
- 如果组未更改,则未来的请求 = 1x 304 Not Modified
- 如果添加了一个新资源,则未来的请求 = 2x 200 OK

结论
最后,您需要考虑您的资源是否足够大以将它们放入子请求中,以及一个请求者重复 bis 组请求的频率(因此使用缓存)。如果没有,您应该将它们包含在群呼中,并且您没有优化的空间。

附:您需要监控所有请求者以确保他们都使用缓存。一种可能的解决方案是禁止请求者在不发送 ETag 的情况下调用 API URL 两次或更多次。

【讨论】:

    【解决方案2】:

    我会采用以下选项之一:

    1. 默认情况下使 ETags 弱,它们是使用资源当前状态生成的,而不是使用 HTTP 响应负载中的资源表示。这样,除了响应标头中整个集合的 ETag 之外,我可以为集合查询响应正文中的每个资源返回一个有效的 ETag。

    2. 在这种情况下忘记 If-Match 和 ETag,并使用 If-Unmodified-Since 和作为每个资源的属性提供的 Last-Modified。通过这样做,我可以保留强大的 ETag,但客户端仍然可以根据集合响应对一个项目进行更新,而无需对资源本身发出另一个请求。

    3. 允许通过 PATCH 更新集合资源本身,将 If-Match 标头与整个集合的 ETag 一起使用。如果有很多并发更改,这可能不会很好地工作,但这是一种合理的方法。

    【讨论】:

      猜你喜欢
      • 2014-03-06
      • 2011-03-03
      • 2011-04-21
      • 2013-10-06
      • 2016-03-30
      • 1970-01-01
      • 2012-05-31
      • 2023-03-07
      • 2011-07-02
      相关资源
      最近更新 更多