我可以将 E-tag 用于用户特定的 REST API,以免每次都发回相同的数据吗?

Posted

技术标签:

【中文标题】我可以将 E-tag 用于用户特定的 REST API,以免每次都发回相同的数据吗?【英文标题】:Can I use E-tag for a user specific REST API in order to not send the same data back every time? 【发布时间】:2021-10-15 05:10:54 【问题描述】:

假设我们有一个检索用户特定数据的 API。 出于某种原因,我不希望服务器每次都将数据发送回客户端,因为数据没有变化。

例如,我有一个移动应用程序。每次启动时,它都会显示本地缓存中的数据,但也会在后台从服务器检索一些用户特定的数据。 我想要做的是希望服务器可以在数据不变的情况下返回 304。

似乎 E-tag 可以做这种事情,但我不确定这是否是一个好的选择,因为它是一个用户特定的 API。

【问题讨论】:

【参考方案1】:

HTTP 是一种请求/响应协议。对于客户端发送的每个请求,服务器都会响应。响应可以是失败也可以是成功案例。除非出现网络中断,否则绝不应该出现服务器不响应的情况!

ETag 特别是通常是资源当前状态的哈希值或预定义的字符串值,即版本计数器(正如 Evert 正确提到的),它在响应中作为 HTTP 标头返回,因此客户端可以在它的情况下使用它想要更改该特定状态,并且不希望服务器在该资源同时被修改时更新它(= 乐观锁定)。

第二种使用 ETag 的情况是检查客户端的状态是否仍然是服务器已知的当前状态,服务器响应 304 Not Modified or a 412 Precondition failed response 取决于资源是否仍然具有相同的 ETag 值或不是。理想情况下,您应该向服务器发出 HEAD 请求,以尽量减少来回发送的有效负载,因为您可能感兴趣的是客户端知道的当前版本是否仍然是服务器持有的版本。

...但我不确定这是否是一个好的选择,因为它是一个用户特定的 API。

...我阅读了一些关于 E-tag 的文章,但它们都没有提到任何关于用户特定数据的内容。 ...

正如Jim Webber 所指出的,从本质上讲,HTTP 只是一个传输协议,其域是通过 Web 传输文档。您最好将其视为文档管理系统,您可以将新文件放在某个位置,删除或检索它们或通过POST 请求根据服务器自己的语义处理它们。 HTTP 基本上就是这样。因此,任何遵守 HTTP 规则的 HTTP 客户端都能够使用 RFC 7232 中指定的条件请求,HTTP 服务器也应该如此。因此,数据来自某个 HTTP Web 服务器还是来自某个 Java、.Net 或任何中间件或框架支持的 API 没有区别,只要它们遵循相同的 HTTP 协议即可。不幸的是,如果您愿意的话,特定框架或实现是否支持这种“功能”是另一回事。

关于使用条件请求是否是一个不错的选择,您有哪些选择?如果客户端想知道它是否具有资源的最新状态,它要么需要礼貌地询问服务器客户端知道的版本是否仍然是最新版本,要么只是(再次)检索整个状态。有些人可能会争辩说,在版本因其他客户端的中间更新而不同的情况下,需要交换多条消息。这是一个有效的论点,尽管您需要估计哪些可能会更频繁地发生。在客户端很少检查其版本是否仍然是最新版本或各种客户端频繁更新该资源的情况下,再次检索该资源的整个状态可能会减少总共交换的字节数,因此最后效率更高一点。尽管 HTTP 提供了 HEAD requests,但您可以利用它来最小化客户端和服务器之间交换的有效负载大小,因为 HTTP 将 HEAD 定义为

...服务器不得在响应中发送消息正文(即响应在标头部分的末尾终止)。服务器应该发送相同的头字段来响应 HEAD 请求,因为如果请求是 GET 则它会发送,除了可以省略有效负载头字段(第 3.3 节)。此方法可用于获取有关所选表示的元数据,而无需传输表示数据...

HEAD 请求消息中的有效负载没有定义的语义;在 HEAD 请求上发送有效负载主体可能会导致某些现有实现拒绝该请求。 (Source)

本质上,HEAD 请求和对此类请求的响应仅包含 HTTP 标头,但没有进一步的消息正文,这有助于显着降低交换消息的字节大小。在最好的情况下,当两个版本相同时,您显着减少了交换的字节数。在最坏的情况下,如果它们不同,那么额外的开销可能可以忽略不计,除非你真的在一个高性能的边缘笼子里。因此,如果您对您当前对某些资源的了解是否仍然是最新的感兴趣,通过HEAD 的条件请求是一个不错的选择 IMO。

【讨论】:

请注意,作为哈希的 ETag 是特定于实现的,但不是必需的。它只需要是一个在响应正文更改时更改的字符串。如果您的 etag 是基于递增的版本号生成的,您可能还会看到例如 ETag: "5" 感谢您的回答。我阅读了一些关于 E-tag 的文章,但它们都没有提到任何关于用户特定数据的内容。我想知道我是否可以使用 MD5 校验和作为 E-tag 值,但也担心它是否会给服务器带来很多负担,因为每次它需要为返回的数据计算 MD5 校验和。

以上是关于我可以将 E-tag 用于用户特定的 REST API,以免每次都发回相同的数据吗?的主要内容,如果未能解决你的问题,请参考以下文章

rest - 将用户限制在自己的数据中的方法

如何在 django rest 框架中仅使用特定变体对象将项目添加到愿望清单?

REST API包含两个字段

REST 查询从解析服务器获取特定用户的所有角色

SOAP 还是 REST?具体项目

从 Django Rest Framework 将经过身份验证的用户传递给 SendBird