改线上页面的样式,我以前的习惯是打开 DevTools 现场改。改是能改,问题是它只活在这一台浏览器、这一个标签页里------刷新一下没了,换台机器得重来,想给别人看效果还得截图。后来我把这件事挪到代理层做,同样的规则一次写好,之后凡是经过这个会话的流量都按它走,包括手机上那台。
抓包工具改响应,按"你想改什么"大致分三类,先给个对照:
| 你想改的东西 | 用哪类动作 | 生效范围 |
|---|---|---|
| 响应头(跨域、缓存、下载方式) | 改响应头 | 所有匹配到的响应 |
| 响应体(JSON、HTML、纯文本) | 改响应体 | 同上 |
| 页面里的 JS / CSS 片段 | 按类型注入 | 只有 Content-Type 对得上时才动手 |
下面挨个说清楚各自的用法和适用场合吧。
改响应头:那些不需要动内容的事
改响应头解决的是"内容没问题,是周边配置不对"这一类麻烦。几个高频的:
跨域(CORS) 。调本地前端连测试环境时最常见的报错,加几个 Access-Control-Allow-* 头就能放通------TraceEagle 里直接提供了放开 CORS 的动作,不用自己拼那一串头。
缓存。改了后端的东西,前端拿到的还是旧的,先怀疑缓存。这类可以按域名把缓存控制改掉(禁缓存,或者改成一个短 max-age),比清浏览器缓存省事,也不会漏掉某个没清的层。
还有些零碎的:接口在浏览器里默认是直接预览的,想让它变成下载,改 Content-Disposition 就行;调试登录态、验证 cookie 作用域的时候要临时塞一个或者改它的域和过期时间,也在这一层做。
除了这几个,增删任意响应头、改状态码、禁 keep-alive、禁压缩这些都能配。规则支持按头值查找替换,所以"把某个头里的值统一换掉"也不用一条条枚举。
改响应体:整份换掉,或者只动一个字段
响应体这一类的动作比较直接:整体替换、查找替换、追加、前置、JSON 浅合并。
这里面最值得说的是 JSON 浅合并。前后端联调时经常遇到这种情况:接口基本是对的,就某一个字段的值不对,或者少了一个字段导致前端走不到某个分支。这时候没必要把整份 JSON 复制出来改完再整体替换------浅合并只写你要改的那几个键,其余保持原样。
当后端还没开发完、或者某个分支的数据一时构造不出来时,"整体替换"更合适:直接返回一份你手写的 JSON,让前端先把逻辑跑通。这类用法和 Mock 假数据是一回事,区别只在于 Mock 是完全不请求服务器、这里是"请求照发,只是响应被换掉"。
两者的差别值得留意:Mock 下服务器压根没收到请求,用它测不到真实的接口耗时、鉴权、埋点这些副作用;改响应则保留了这次真实请求,只是把回来的东西换了。选哪个,看你要验证的是什么。
按类型注入:专治页面里的 JS 和 CSS
这一类比较特别,它不是按域名匹配,而是按响应类型匹配------只有当响应的 Content-Type 是 HTML、JS 或 CSS 时,才会往里面注入或替换脚本、样式、片段。
为什么要有这类呢?因为改页面上的一段 CSS,直接整体替换响应体是很麻烦的:那份 CSS 可能有几千行,你还得先把它完整抓下来。按类型注入只要写"往 CSS 里追加这几行"就够了,匹配上就注入,匹配不上什么都不做。
几个实际会用的场景:
- 线上样式出问题,先验证修复。往线上页面的 CSS 追几行覆盖样式,看改完是不是就好了------确认之后再走正式发版流程,比"盲改发版再看"稳得多。
- 往线上页面塞一段调试脚本。比如打点日志、打印某个全局变量的值、给按钮挂个临时监听。改的是你本机的流量,线上代码一行没动。
- 给某个 JS 文件打补丁。用查找替换把文件里的一小段换掉,验证"如果这里改一下会怎样"。
有一点必须说清楚:这些改动只在你自己这台机器的抓包会话里生效。服务器上的代码没变,别人的请求也不受影响,关掉会话就恢复原样。它是调试手段,不是发布通道------验证完还是得把改动落地到代码里。
规则怎么写
上面这些动作,在 TraceEagle 里写起来都是一行一条的形式:<匹配> <动作>://<值>。举个例子,给某个测试域名放开跨域、同时把它的一部分响应体换掉:
css
*.test.example.com cors://
*.test.example.com resBody://{"code":0,"data":null}
匹配那一层给得比较细:精确域名、通配子域、正则匹配整条 URL、精确到端口,还能用 ! 取反("除了这些")。规则支持分组和注释,保存即热生效,不用重启会话。
值里还能用动态变量,比如 ${url}、${host}、${method}、${status}、${now}、${randomUUID},以及读某个请求头或响应头的 ${req.headers.X}、${res.headers.X}。常用值可以定义成命名值,写一次到处引用。记不住语法也不碍事------界面上有一份可点击的动作速查表,点一下把示例插进来改值就行。
在 TraceEagle 里,这套规则和弱网模拟、转发、断点挂在同一个体系下:同一个域名可以一条规则限速、一条规则改响应头、再来一条把它映射到本地开发服务器,彼此不打架。
和浏览器本地覆盖、和别的工具比一比
浏览器 DevTools 其实也有本地覆盖(Overrides)的能力,可以把线上文件替换成本地的一份。它的好处是不用额外装东西、就在手边;局限是作用范围只在浏览器自己,手机上、原生 App 里的页面、非浏览器发的请求都够不着,而且它替换的是整个文件,不像按类型注入那样可以只追加几行。
Charles 的 Rewrite 和 Map Local、Fiddler 的 AutoResponder 走的是同一条路------都在代理层改响应,能力和适用场合都接近,差别主要在规则表达上(能不能按类型注入、动态变量丰不丰富、能不能和别的能力共用一套规则)。mitmproxy 用脚本实现同样的效果,灵活度最高,代价是得写代码。
选哪条还是看你手上是什么嘛:只在自己浏览器里看个效果,DevTools 就够了;要覆盖手机、App 内嵌页、命令行程序,或者要按响应类型精细注入,代理层更合适。