开始前,会这些就够了
Node.js20 或更高版本- 会在
PowerShell中打开文件夹并运行命令
遇到不熟悉的词,先看这 2 个解释
- HTTP 请求与响应
- 客户端用请求说明想访问什么、做什么;服务端用响应说明处理结果,并按需要带回内容。一次网页操作可能触发多组这样的消息交换。
- HTTP 状态码
- 响应里的三位数字,用来说明这次请求处理到了哪种结果,例如成功、需要重定向或发生错误;程序会结合响应内容决定下一步怎样处理。
刷新页面后,网络面板里有时会出现状态码 304,看起来像服务器没有返回内容。它并不表示文字被删了,而是在合适的条件下告诉客户端:你保存的那份还可以继续使用。要看清这件事,可以自己控制文字和请求,避免一开始就被浏览器的自动行为打断思路。
这个配套项目只有服务器、验证脚本、中文说明和一次验证记录。它不需要安装额外依赖,也不连接外部服务。你会先预测一次请求的结果,再亲手改变一段教学文字,最后用旧标签和新标签分别询问服务器。
把例子带到自己的电脑上
下载完整练习包
需要 Node.js 20 或更新版本。使用标准库,不需要安装第三方包。下载 ZIP 后选择“全部提取”,进入能看到 README.md 的文件夹。
下载 ZIP · 4 个文件包里有什么
- server.cjs:本机教学服务器
- verify.cjs:自动验证
- README.md:中文操作步骤
- verification-result.json:验证记录
在这个文件夹打开 PowerShell,逐行运行
node --version
node verify.cjs先运行自动验证。想自己发送请求时,再运行 node server.cjs,按 README 在另一个 PowerShell 窗口操作;结束时回到服务窗口按 Ctrl+C。
找不到命令或文件时,先按 README 检查环境和所在目录。文中标出的阅读时间只估算文章篇幅,安装环境和动手练习可按自己的节奏进行。
先让项目在自己的电脑上通过检查
下载配套文件并解压,在包含server.cjs和verify.cjs的文件夹打开PowerShell。先运行node --version确认版本,再运行node .\verify.cjs。验证脚本会自己选择可用端口、启动独立服务并检查响应,结束时只关闭它创建的进程。此时不必手动启动服务器。
末行显示passed,才表示这一轮检查完成。下载包中的verification-result.json只是制作时的记录,不能代替你电脑上的结果。如果出现FAIL,应先读失败项;不要为了避开端口问题结束其他程序,也不要把还没启动成功当成已经验证了缓存。
从第一次拿到文字开始
接着在第一个窗口运行node .\server.cjs。服务默认使用本机地址和3087端口,窗口中的ready行给出实际端口。再打开第二个PowerShell窗口,按README中的完整步骤创建客户端。那段说明保留标签里的双引号,并关闭旧版PowerShell客户端自动添加的上传预告。
先不带任何旧标签读取/message,记录状态、正文和ETag。ETag可以理解为这份内容的版本标签。本项目按实际发送的字节计算标签,所以改一个空格也算改动。它不是账号凭据;这里只用它比较教学文字,不把它当成身份验证。
带着旧标签再问一次
现在先写下预测:内容未改时,带刚才的标签请求,会不会再次收到整段文字?再执行README中的条件请求。请求头If-None-Match携带已有标签,服务器按支持的规则比较,匹配时发送304,响应中没有正文。客户端若需要显示原文,应使用先前保存的内容。
再比较HEAD请求。它只询问响应头信息,成功返回200时同样没有正文,因此不能只凭正文为空判断缓存命中。还要一起看状态和标签。这里的no-cache要求复用前向服务器验证,不是禁止保存;命令行客户端用于观察响应,没有替你实现完整浏览器缓存。
只改一个条件,观察旧标签失效
通过README给出的PUT步骤,把教学文字改成第二版。然后保留最早的标签,再读同一个地址。比较新旧状态、标签和正文,接着用刚收到的新标签再请求一次。整个过程中只改变文字或请求标签,地址和运行中的服务器保持不变,才能说明结果差别来自哪里。
更新只存放在内存里,重启就恢复第一版。正文必须是合法的UTF-8纯文本,最多两千零四十八个字节,不能全是空白。服务器收完并检查后才替换内容;无效字节、过长或超时的上传会失败,不能把失败响应误认为新文字已经生效。
把观察与实验的边界一起记下来
条件头只支持README列出的有限形式:单个带引号标签、弱标签、有限数量的标签列表或单独星号。完整协议允许的形式更多。其他条件请求与带条件的更新会明确被拒绝;这个短项目的作用是看懂流程,不能直接充当通用缓存服务器。
实验后在服务器窗口按Ctrl+C退出,并关闭客户端。这里没有数据库、登录或多人同时编辑保护,也没有测量网络加速倍数。你得到的是一组能重新执行的观察记录:请求带了什么、响应是什么、正文有没有传输,以及停止服务后哪些数据不会保留。
记住这三点
- 同时记录状态、ETag和正文长度,不能用“没有正文”单独判断缓存命中。
- 改变文字后,分别用旧标签与新标签请求,才能看清条件请求的作用。
- 自动验证只操作它启动的本地进程;手动更新只在本次服务器运行期间有效。
别把例子用过头
- 304没有正文不等于资源不存在,HEAD 200也没有正文。
- 本项目的条件头解析器有明确子集,不能推广为完整HTTP实现。
- 字节限制不同于字数限制;中文、英文和换行占用的字节可能不同。
自己试一小步
如果先改成第二版,再逐字改回第一版,最早保存的标签还会得到什么结果?
- 保存第一版正文与标签,先写下往返修改后的状态和正文长度预测。
- 按README的更新步骤改成第二版,再恢复完全相同的第一版,包括末尾换行。
- 最后加一组只修改空格的操作,逐次记录请求方法、标签、状态与正文长度。
做到这些,就算完成
- 预测与实际记录分开,能定位到具体哪一次请求。
- 能解释屏幕看起来相近与字节完全相同的区别。
- 实验完成后退出服务,再确认哪些变化只存在于先前的内存中。
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- RFC 9110:条件请求与304 (新标签页)↗
If-None-Match采用弱比较,GET
/HEAD匹配后的304及无正文要求。 - RFC 9111:HTTP缓存 (新标签页)↗
no-cache要求复用前验证,与no-store的区别。
- Node.js 20 HTTP接口 (新标签页)↗
标准库请求与响应、请求体流、响应头与关闭连接接口。
- Node.js子进程 (新标签页)↗
验证脚本启动自己的子进程、使用进程间通信并等待退出。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
浏览器网络面板:查清哪个请求慢了或失败了 (新标签页)↗
完成本地脚本后,只练网络面板的请求记录与响应头检查,把实验中的 200、304 和版本标签对应到浏览器里。
查看学习准备与访问说明 →