让 AI 改造订阅抓取器:用 ETag 节省重复下载
保存 ETag,使用条件请求判断更新。

订阅源定时检查多数时候只是在确认没变化。如果对方返回 ETag,可以把它和上一次内容一起保存,下次带 If-None-Match。服务端判断资源未变化时可以返回 304。
让助手从这一步开始
给 Codex 一份本地 HTTP 服务和旧缓存格式,要求实现条件请求,只在新正文解析成功后同时写入正文和标签。请它列出上游不发送 ETag、已有标签但正文丢失这两种恢复路径。

把标签和正文当成一对
收到新内容并验证成功后,同时保存正文和标签。收到 304 则沿用上次内容,并记录本次检查成功。别把 304 当成空订阅,也别在解析失败时只更新标签,否则下次可能找不回对应正文。
测试用三次请求串起来:先返回一份内容和标签,再返回 304,最后返回新标签和新内容。每一步都核对正文和检查时间。
如果没有旧正文,发一次不带条件的请求。条件请求的前提是自己确实存有可以复用的结果。
把问题变成可重跑的样例
把测试响应固定成 200 ETag: "feed-1"、304、200 ETag: "feed-2" 三次。让助手在本地服务记录后两次的 If-None-Match,从缓存重新读出标题。最后追加一次没有旧正文的启动,验证客户端会发无条件请求。
核对这几项输出
- 第一次返回 200 和 v1 时存一份快照。
- 第二次 304 时更新检查时间且保留正文。
- 第三次 200 带 v2 但正文破损时保留 v1 和原标签,并记录错误。
怎么判断改动能不能收
检查的是请求头、落盘文件和更新时间,不能只看日志写了「缓存命中」。如果定时任务并发执行,先约定同一来源的写入顺序。也要把 304 与 429 分开处理,前者有可用旧正文,后者表示本轮被限速。
检查助手是否用 response.ok 在最前面统一抛错。304 在这个属性的成功区间之外,应先按条件响应单独处理。再要求模拟资源更新后变成没有 ETag 的 200,这时应清掉旧标签,下一次重新取正文。旧缓存标签和当前内容必须来自同一版本。
这段代码展示一次检查返回的快照。调用方在校验完成后一次性替换缓存。
const headers = cached?.body && cached?.etag
? { 'If-None-Match': cached.etag }
: {};
const response = await fetch(url, { headers });
if (response.status === 304) return { ...cached, checkedAt };
if (!response.ok) throw new Error('HTTP ' + response.status);
const body = await parseAndValidate(response);
return { body, etag: response.headers.get('etag'), checkedAt };
