【问题标题】:how does a etag work in expressjsetag 如何在 expressjs 中工作
【发布时间】:2014-08-23 22:38:19
【问题描述】:

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

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

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

【问题讨论】:

  • 您能否澄清一下您的第二段和第三段?最好能详细了解您的特定问题。
  • 我把你的问题读了五遍,并为登陆这里的人提供了关于 Etag 下面是什么的详细答案。在您的特定情况下,我认为您混淆了四个概念:Etag、SessionId、身份验证、授权。我强烈建议不要为其他人重复使用其中的任何一个。请记住“过早的优化是万恶之源!” -- softwareengineering.stackexchange.com/q/80084

标签: express cache-control etag if-modified-since


【解决方案1】:

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

Based on what one of the contributors to Express says,您可以通过以下方式指定是使用强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

【讨论】:

  • 我要指出的是,根据express 4.X docs,启用etags时默认etag类型是weak
  • 更新:ETag 生成由jshttp/etag 模块完成。版本 1.7 (2015-06-08) 总是使用 MD5 而不是 CRC32,因为 CRC32 容易发生冲突。下一个版本将始终使用 SHA1 而不是 MD5,因为 MD5 不符合 FIPS。最后,“weak”唯一要做的就是在 ETag 上设置“W/”前缀。虽然 express 默认为“W/”,但它在技术上是一个强大的 ETag,因为哈希是根据正文字节而不是其语义内容计算的。如果您能够为您的应用实现弱 ETag 计算,那么这可能会大大提升性能。
  • 2014 年的好答案!对于 2021 年的答案以及更多详细信息,请参阅另一个答案。
【解决方案2】:

让我在 2021 年解释一下,提供更新的信息和代码链接。

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

什么是 Etag?

所以,Etag(每个 Wikipedia/Etag)是一个 HTTP 标头。

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

在Express中,它可以以W/(弱,默认)或非(强)开头,然后是<LEN>-<VALUE>,其中VALUE为27个字符长,LEN为十六进制值。 (Source code in June 2021)

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 的同一端点,它可以告诉服务器“嘿,我有这个 在我的缓存中,所以如果你要发送的正是这个,请不要发送它。"

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

是的!

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

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

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

不错!是这样吗?

嗯,在上面的例子中,如果你仔细观察,哈希计算发生在客户端和服务器端。至少,这会使更改算法变得困难。所以,实际上,“响应的哈希”实际上也是第一次在服务器端计算,并将发送回客户端。

随着响应返回的“当前响应”的计算哈希值位于 响应ETag 标头中。

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

然后,在以后的任何请求中,客户端都会将此 ETag 值发送到服务器(在某些标头中,例如 if-none-match),这意味着如果新调用的响应将具有ETag 这个。

所以,回顾一下:

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

太棒了!我该如何使用它?

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

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

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

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

快递包usesetag package(它只是一个文件,由同一个人管理)以生成ETag值。在内部,etaguses sha1body 进行加密,并没有什么疯狂的,以保持最佳性能。 (如果你想象,这个函数会被调用很多次!服务器接收和处理的每个 any GET 调用平均至少有一次或两次。)

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

酷,那有什么问题?

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

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

EXPRESS 的当前 Etag 实现,仅依赖于响应体,而不依赖于响应头。 甚至,它们允许实施的自定义功能(doccode)也只需要正文内容,而不是响应标头。

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

如何禁用它?

您可以使用app.set("etag", false);,以便 Express 停止发送它。根据this answer,您还可以/应该通过app.use(nocache()) 使用nocache 来发送“嘿,客户,不要打扰自己缓存它!”从服务器到客户端的标头。

干杯!

PS。最后说明:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-27
    • 1970-01-01
    • 2011-08-06
    • 2014-10-24
    • 1970-01-01
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多