Expressjs自动发送etags。我想知道如何生成etag ..它是基于get例程动态生成的内容。或者我是否可以通过生成内容(动态内容 - 来自数据库)的过程来维护它,并将etag传回去。
可能是一个中间件,它只是检查它是否是有效的会话ID并传回客户端提供的相同etag,或者可能基于url +会话ID ...这将是唯一的。并结束请求,而不是通过整个数据库调用和所有这些东西。在这种情况下,我需要知道客户端正在拨打304电话。
我可以使用expires标签。但是当会话结束时。如果有人打开网址,它不应该允许。所以我认为etag也应该基于会话ID。如何修改可以在此动态内容方案中工作。可以使用吗。
答案 0 :(得分:38)
在撰写本文时(2014年7月8日),使用CRC32(source)生成弱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中用于新鲜度检查(source1,source2)。
至于您的应用程序,请记住您可以覆盖任何响应标头,例如在调用res.set('etag', 'my-awesome-etag-value')
(或类似函数)之前res.send()
。可以在此处找到进一步的讨论(包括优点和缺点):https://github.com/visionmedia/express/issues/2129#issue-34053148
答案 1 :(得分:2)
让我在 2021 年解释它,提供更新的信息和代码链接。
这是一个相对直接和简单(非火箭科学)的概念,但同时也是一件非常棘手的事情,作为一名开发人员,在它咬你之前你应该真正了解它!
因此,Etag(根据 Wikipedia/Etag)是一个 HTTP 标头。
它可以在 DevTools 中某些 GET 调用的“响应标头”部分看到,如下面的屏幕截图。
在 Express 中,它可以以 W/
(弱,默认)或不(强)开头,然后是 <LEN>-<VALUE>
,其中 VALUE 为 27 个字符长,以及 LEN是 VALUE 的长度,以十六进制表示。 (Source code in June 2021)
啊,好问题。答案是:缓存!
(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 中。所以,坐下来享受吧!
您不太可能需要弄乱其设置。
啊!欢迎来到我的生活。 :D 这就是我来到这里并完成所有这些研究的方式。
Express 将 uses 和 etag package 打包(它只是一个文件,由同一个人管理)以生成 ETag 值。在内部,body 的 etag
包 uses 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 来发送“不要打扰自己缓存它!”从服务器到客户端的标头。
干杯!