etag如何在expressjs中工作

时间:2014-07-03 00:02:02

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

Expressjs自动发送etags。我想知道如何生成etag ..它是基于get例程动态生成的内容。或者我是否可以通过生成内容(动态内容 - 来自数据库)的过程来维护它,并将etag传回去。

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

我可以使用expires标签。但是当会话结束时。如果有人打开网址,它不应该允许。所以我认为etag也应该基于会话ID。如何修改可以在此动态内容方案中工作。可以使用吗。

2 个答案:

答案 0 :(得分:38)

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

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

答案 1 :(得分:2)

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

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

什么是 Etag?

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

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

enter image description here

在 Express 中,它可以以 W/(弱,默认)或不(强)开头,然后是 <LEN>-<VALUE>,其中 VALUE 为 27 个字符长,以及 LEN是 VALUE 的长度,以十六进制表示。 (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 的同一个端点,它可以告诉服务器“嘿,我有这个 < previous_json_from_the_cache> 在我的缓存中,所以如果您要发送的内容正是这个,请不要发送它。"

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

是!

问题是,如果客户端从缓存中发送整个 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 这就是我来到这里并完成所有这些研究的方式。

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

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

酷,那有什么问题吗?

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

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

Express 的当前 Etag 实现,仅依赖于响应体,而不依赖于响应头。甚至,它们允许放置的自定义函数 (compares, doc ) 仅获取正文内容,而不获取响应标头。

因此,当响应(例如个人资料数据)未更改时,您的客户端可能会重复使用过时的身份验证令牌并因身份验证/会话标签无效而将用户踢出!

如何禁用它?

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

干杯!

附注。最后说明:

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