血泪史
最近在学习强缓存和协商缓存,看了好多文档,千篇一律都说:服务器会根据前端请求头中携带的If-None-Match内容和后端的文档唯一标识Etag作对比,来决定返回304还是200状态码。
我就秉承着实事求是的态度,去验证,搭了前后端的代码(后面贴上前后端代码),顺带学习配了跨域处理。结果好家伙!!!确实是有那个先比对强缓存,强缓存过期后再比对协缓存的过程和逻辑,就是浏览器显示的状态码不对,从来不显示304,只显示200,只有document类型的数据会正常显示服务器返回304。
最可气的是,我问国内的AI,AI说是我的代码有问题,改了一个多小时,最后使用postman去直接发请求,绕过浏览器,手动为header添加一个If-None-Match,终于捕捉到服务端返回的状态码304。最终我得出结论:浏览器对服务器匹配到的协商缓存返回的304状态码做了一次转换,转换成了200,这种转换对某些类型的请求不转换,比如document。
为了再次验证我的结论,我去科学上网问gpt4,好家伙确实浏览器会对服务端返回的304给转换成200,比如css,图片,js文件也会进行状态码转换,但是document不会。
为什么要选择性进行状态码转换?
转换的原因
浏览器确实对协商缓存的返回结果的状态码做了一次转换。这主要是为了提供更好的用户体验。
304状态码是HTTP协议中的一部分,主要用于服务器和浏览器之间的通信。当服务器返回304状态码时,它告诉浏览器请求的资源没有被修改,可以使用缓存的版本。然而,对于用户来说,他们并不关心资源是从服务器获取的还是从浏览器缓存中获取的,他们只关心资源是否获取成功。因此,浏览器在从缓存中获取资源后,会把状态码改为200,表示资源获取成功。
此外,使用200状态码还有一个好处,那就是它可以让开发者更容易地理解和调试问题。如果浏览器直接显示304状态码,开发者可能会误以为资源没有成功获取,因为在HTTP协议中,只有200状态码表示请求成功。通过将状态码转换为200,浏览器可以避免这种误解。
总的来说,浏览器对协商缓存的返回结果的状态码做了一次转换,主要是为了提供更好的用户体验和方便开发者调试问题。
document不转换的原因
对于页面(document)类型的请求,浏览器通常会直接显示服务器返回的状态码,也就是304,这是因为这种类型的请求通常表示一个全新的页面加载,浏览器会尽可能地提供更多的信息,以便于开发者对页面加载过程有更全面的理解。
贴上我的前后端代码
大家有兴趣的自行验证,postman返回的是304,下面是postman截图。绝不骗人!我比AI实诚哈哈哈
前端就一个html文件,用vscode插件,live server打开的话默认会在浏览器中开启一个5500的前端服务端口打开
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script src="https://lib.baomitu.com/jquery/3.5.1/jquery.js"></script>
<script>
$.ajax({
type: "get",
url: "http://127.0.0.1:5560/example-resource",
success(res) {
console.log(res);
}
});
</script>
</body>
</html>
后端nodejs
const express = require("express");
const cors = require("cors");
const path = require("path");
const fs = require("fs");
const crypto = require("crypto");
const app = express();
//处理跨域
app.use(
cors({
origin: ["http://127.0.0.1:5500", "http://localhost:5500"] // 跨域白名单
})
);
app.get("/a", (req, res) => {
res.status(200).json({
msg: "dddddd"
});
});
//设置缓存倒计时10秒,可以自己改
const ONE_DAY_IN_SECONDS = 10;
app.get("/example-resource", (req, res) => {
//读取根目录public文件夹下的example.txt路径
const filePath = path.join(__dirname, "public", "example.txt");
// 生成或读取资源内容,这里简化处理直接返回文件
const fileContent = fs.readFileSync(filePath, "utf8");
// 设置强缓存
res.setHeader("Cache-Control", `public, max-age=${ONE_DAY_IN_SECONDS}`);
// 生成ETag,用于协商缓存
const etag = crypto.createHash("md5").update(fileContent).digest("hex");
console.log(req.headers["if-none-match"]);
res.setHeader("ETag", etag);
// 检查协商缓存
if (req.headers["if-none-match"] === etag) {
console.log("match");
// 如果ETag匹配,表示资源未更改,返回304
res.status(304).send();
} else {
// 否则,返回资源内容
res.send(fileContent);
}
});
app.listen(5560, function () {
console.log("CORS-enabled web server listening on port 5560");
});
有收获的话,可以点个赞哟