我正在尝试了解缓存标头和响应代码的行为。
最初我有一个如下所示的快速应用程序:
app.get("/users-a", async (req, res) => {
await new Promise((res) => setTimeout(res, 3000));
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Cache-Control", `public, max-age=10`);
res.json(users);
});
本质上,我们模拟了需要 3000 毫秒的“数据库查找”,然后返回响应。响应缓存标头是
public, max-age=10
。
在浏览器中,我现在重复单击按钮来发出此请求,我们看到第一个请求需要 3000 毫秒,后续请求是立即的 - 它们使用浏览器的磁盘缓存,但 10 秒后发出 3000 毫秒的请求再次。
请注意,响应包含标头:
Etag: W/"1a-Q1ZzAew6rLbf4JEZbVarkQiSXKo"
这个Etag是由express自动应用的,请参阅这里的全面讨论:
请求包含标头:
If-None-Match: W/"1a-Q1ZzAew6rLbf4JEZbVarkQiSXKo"
到目前为止,这对我来说都是有意义的。
我现在想模拟浏览器,现在有过时的数据,发出该请求并接收 304 状态代码 - 指示它使用其现有内容。
我们修改申请如下:
app.get("/users-b", async (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const ifNoneMatch = req.headers["if-none-match"];
if (ifNoneMatch && etags.has(ifNoneMatch)) {
res.sendStatus(304);
} else {
await new Promise((res) => setTimeout(res, 3000));
res.setHeader("Cache-Control", `public, max-age=${10}`);
res.json(users);
const etagValue = res.getHeader("etag");
etags.add(etagValue);
}
});
这很粗暴,但本质上我们将express生成的etag存储到一个集合中,然后如果收到带有if-none-match header的请求,则立即返回304,没有响应正文 - 客户端已经有了它.
我期望看到的是,在浏览器中我们会看到 304 响应。当然,快速代码确实沿着
res.sendStatus(304)
路径执行。
但我们看到的是,在网络选项卡中,浏览器有一个 200 响应,带有响应正文。响应不需要 3000 毫秒,表明缓存行为正常工作。
我想我的问题是 - 这就是浏览器对 304 状态代码的行为方式吗 - 它会将其转换为 200 响应而不向您显示?
根据 Kevin Henry 的评论,这似乎是 Chromium 中的一个错误。
我已经在 Chrome 129 中进行了测试,它似乎可以正常工作。