62

Expressjs 自动发送 etags。我想知道etag是如何生成的。它是基于get例程动态生成的内容。或者有没有办法我可以维护它,甚至不经历生成内容的过程(动态内容 - 来自数据库)并将etag传回。

可能是一个中间件,它首先检查它是否是有效的会话 id,然后传回客户端提供的相同 etag,或者可能基于 url + 会话 id。这样​​它将是唯一的。并在那里结束请求,而不是通过整个数据库调用和所有这些东西。在这种情况下,我需要知道客户正在拨打 304 电话。

我可以使用 expires 标签。但是当会话结束时。如果有人打开它不应该允许的 url。所以我认为etag也应该基于会话ID。如果修改后如何在这种动态内容场景中工作。可以用吗。

4

2 回答 2

57

在撰写本文时(2014 年 7 月 8 日),弱 ETag 使用CRC32来源)生成,而强 ETag 使用 MD5(来源)生成。

根据 Express 的一位贡献者所说,您可以通过以下方式指定是使用强 ETag 还是弱 ETag:

app.enable('etag') // use strong etags
app.set('etag', 'strong') // same
app.set('etag', 'weak') // weak etags

看起来您还可以指定自己的自定义函数来执行 ETag,如下所示:

app.set('etag', function(body, encoding){ /* return valid etag */ });

NPM 包fresh也值得一看,因为它在 Express 中用于新鲜度检查(source1source2)。

至于您的应用程序,请记住您可以覆盖任何响应标头,例如res.set('etag', 'my-awesome-etag-value')在调用res.send()(或类似功能)之前。进一步的讨论(包括优点和缺点)可以在这里找到:https ://github.com/visionmedia/express/issues/2129#issue-34053148

于 2014-07-08T08:54:15.707 回答
36

让我在 2021 年用更新的信息和代码链接来解释它。

这是一个相对直接和简单(不是火箭科学)的概念,但同时也是一个非常棘手的事情,作为开发人员,在它咬你之前你应该真正知道!

什么是埃塔格?

因此,Etag(根据Wikipedia/Etag)是一个 HTTP 标头。

它可以在 DevTools中一些GET调用的“响应标头”部分看到,如下面的屏幕截图。

在此处输入图像描述

在 Express 中,它可以以W/(weak, default ) 或非 (strong) 开头,然后<LEN>-<VALUE>是 ,其中 VALUE 是 27 个字符长,LEN 是 VALUE 的十六进制长度。(2021年6月源码

Etag的目的是什么?

啊,好问题。答案是:缓存!

(PS。并且仅缓存客户端和服务器之间的网络流量。这是响应数据的传输,通过HTTP(S)发送到客户端;不是任何类型的服务器到数据库的内部缓存或其他。 )

缓存,怎么做?

机制比较简单。

假设一个客户端(浏览器,如 Chrome)调用https://myserver.com/user/profile/get端点并获取当前用户的所有配置文件数据的大型 JSON 响应(例如,姓名、电话、照片 URL、等等等 30 个字段)。除了将响应作为 JSON 对象传递给您的应用程序之外,客户端在其自己的私有内部网络层中,还会将此数据存储在{'https://myserver.com/users/profile/get': <this-json-response-object> }.

现在,下一次(甚至几天和会话之后)客户端将要调用 的同一个端点.../user/profile/get,它可以告诉服务器“嘿,我的缓存中有这个 <previous_json_from_the_cache>,所以不要发送如果你要发送的正是这个,那就结束吧。

很酷,但这不是效率低下吗?

这是!

问题是,如果客户端在向服务器的请求中从缓存中发送整个 JSON 对象,这既存在安全风险,而且效率非常低——通过网络发送相同的 30 字段 JSON 对象,甚至可能两次!

这里发生的是,客户端(即 Chrome 浏览器)可以计算一个哈希值(比如 MD5,它既不可逆又更短),并在第二个请求中说“嘿,如果你要发送的 JSON 的 MD5 哈希值我回来的就是这个<computed_hash>,我已经有了!你不要发过去。

现在,发生的事情是,服务器将像以前一样计算响应(从数据库和所有内容中提取)。但是,仅在发送响应数据之前,它会计算响应的哈希值(在服务器端),以查看它是否与客户端所说的匹配。如果是这样,它会发送一个 304 HTTP 状态响应代码,而不是 200,这意味着“没有任何改变”。

好的!是这样吗?

好吧,在上面的示例中,如果您密切注意,哈希计算同时发生在客户端和服务器端。至少,这会使更改算法变得困难。所以,实际上,“响应的哈希”实际上也是第一次在服务器端计算,并将被发送回客户端。

与响应一起返回的“当前响应”的计算哈希位于响应的ETag头中。

这样,每当客户端收到响应时,它将存储:{ ".../profile/get": [<ETag>, <JSON-Response-Data>] }在其内部缓存中。

然后,在任何未来的请求中,客户端都会将此值发送到ETag服务器(在某些标头中if-none-match,如ETag

所以,回顾一下:

  • ETagvalue 并不疯狂,而是响应数据(主体)的不可逆、短且快速的散列值。
  • ETag服务器在响应中向客户端发送标头。
  • 客户端在请求中向服务器发送if-none-matched标头(其值是先前Etag从服务器接收到的值)。

伟大的!我该如何使用它?

默认情况下,它发生在 Express.js 中。所以,坐下来享受吧!

您不太可能需要弄乱它的设置。

我什么时候不应该使用 Etag?

啊! 欢迎来到我的生活。:D 这就是我来到这里并进行所有这些研究的方式。

Express 包使用etag(它只是一个文件,由同一个人管理)来生成 ETag 值。在内部,该etag使用 主体sha1加密,并没有什么疯狂的,以保持最佳性能。(如果你想象,这个函数会被调用很多次!服务器接收和处理的任何GET 调用平均至少一次或两次。)

为了决定它应该执行 304 还是 200,当客户说“我的缓存中已经有了这些值”时,Express 使用新的包(同样只有一个文件,实际上只有一个返回布尔值的函数,由同一个人维护)。在内部,fresh包读取if-none-matched请求标头 ( reqHeaders['if-none-match'])的标记并将其与将要发送etag的响应 ( ) 的标记进行比较。resHeaders['etag']

酷,那有什么问题吗?

当您的架构以及客户端和服务器之间的通信依赖于自定义标头时,就会出现问题!

例如,您想在任何请求上更新身份验证或会话令牌,并在后台刷新它并发送一个新的,作为某些请求的响应标头。

当前 Etag 的 EXPRESS 实现,仅依赖于响应体,而不依赖于响应头。 甚至,他们允许放置的自定义功能(doccode)仅采用正文内容,而不是响应标头。

因此,发生的情况是,当响应(例如配置文件数据)未更改时,您的客户端可能会重用过时的 auth-token 并由于无效的 auth/session 标签而将用户踢出!

我怎样才能禁用它?

您可以这样app.set("etag", false);做 Express 停止发送它。根据这个答案,您可以/应该也使用nocache viaapp.use(nocache())来发送“嘿客户,不要打扰自己缓存它!” 从服务器到客户端的标头。

干杯!

PS。最后说明:

  • 如果你仔细想想,ETags 对于资产(当响应数据的大小为 100KB 或更大时)非常有价值,但对于常见的 API Endpoints 数据则不然。因此,为您的小响应端点禁用它可能不是一个坏主意——实际上,不支付开销可能值得。
于 2021-06-10T23:50:24.270 回答