如何识别客户端原始HTTP请求并实现请求数据的暂存与替换
"为显示网页,浏览器会发送原始请求以获取代表该页面的HTML文档。随后浏览器会解析该文件,发起对应执行脚本、布局信息(CSS)以及页面包含的子资源(通常为图片和视频)的额外请求。"
上述引用来自MDN Web Docs HTTP概述
技术需求
希望识别客户端发送的原始HTTP请求,暂存该请求及其向服务器发起的所有子请求;当客户端发起新的原始请求时,用新的请求数据替换原有暂存数据。
举例说明:假设有一个html页面,客户端解析它时会向服务器发起多个资源请求;当用户刷新该页面时,即发起新的原始请求,此时需用新的原始请求及其子请求数据替换原有暂存数据;请求其他html页面时也遵循此逻辑。
实现思路与方案
核心逻辑
要实现这个需求,关键是准确区分原始请求和子请求,同时维护一个暂存容器——每次新的原始请求进来,就清空旧数据,把新的请求组存进去。
原始请求的判定标准:一般是浏览器获取HTML文档的请求,要么响应的Content-Type为text/html,要么请求的Accept头包含text/html;子请求则是该HTML加载后触发的脚本、样式、图片等资源请求,这类请求的Referer头通常指向对应的原始HTML页面。
具体实现方式
方式一:用抓包代理工具快速验证
比如用Mitmproxy或Charles,通过简单脚本即可实现:
- 维护一个全局变量存储当前请求组
- 拦截所有请求,当检测到响应为
text/html类型时,清空旧的暂存数据,将该请求标记为"原始请求"存入容器 - 后续拦截到的请求,若
Referer与当前原始请求的URL匹配,则作为子请求追加到当前请求组 - 新的
text/html请求到来时,重复清空-存入流程
Mitmproxy的Python核心脚本片段:
current_session = [] current_root_url = "" def request(flow): global current_session, current_root_url # 识别原始请求:响应为HTML文档 if flow.response and flow.response.headers.get("Content-Type", "").startswith("text/html"): # 重置暂存数据 current_session = [] current_root_url = flow.request.url # 存入原始请求信息 current_session.append({ "type": "root", "url": flow.request.url, "method": flow.request.method, "headers": dict(flow.request.headers) }) # 识别子请求:Referer指向当前原始请求 elif current_root_url and flow.request.headers.get("Referer", "") == current_root_url: current_session.append({ "type": "sub", "url": flow.request.url, "method": flow.request.method, "headers": dict(flow.request.headers) })
方式二:自定义后端中间件嵌入服务
如果是在自有后端服务中实现,可以添加一个中间件:
- 用内存变量或Redis存储请求组,给每个客户端分配唯一标识(如Session ID),避免不同用户的数据混淆
- 中间件拦截所有请求,判断是否为原始HTML请求:检查响应的
Content-Type或请求的Accept头 - 识别到原始请求时,根据客户端标识清空对应旧数据,存入原始请求信息
- 子请求通过
Referer头匹配当前客户端的原始请求URL,追加到暂存容器中
关键注意事项
- 多客户端隔离:不同用户的请求需分开暂存,可通过Session或IP+UA的组合标识区分
- 异常处理:若原始请求加载失败,需及时清空无效的暂存数据,避免影响后续请求
- 缓存兼容:浏览器缓存HTML文档时,普通刷新可能不会发起新请求,需识别强制刷新(如Ctrl+F5)的请求
内容的提问来源于stack exchange,提问作者Ibrahim Sharaf
相关产品推荐
相关产品推荐

