Whistle 前端调试代理实战技巧

Whistle 前端调试代理实战技巧

Whistle 是基于 Node.js 的跨平台抓包代理工具,支持 HTTP/HTTPS/WebSocket 拦截、请求转发、本地文件替换、接口 Mock,是前端日常调试利器。本文整理实战高频用法、踩坑难点、缓存问题终极方案。

官方文档:https://wproxy.org/

目录

  1. 快速安装与启动
  2. HTTPS抓包:证书正确安装(高频踩坑)
  3. 核心界面功能说明
  4. 常用基础规则大全
  5. 重点场景:本地静态文件替换(file://)
  6. [Values 面板深度使用](#Values 面板深度使用)
  7. [进阶:module:// 动态脚本处理请求](#进阶:module:// 动态脚本处理请求)
  8. 经典疑难问题汇总
  9. 最佳实践模板(直接复制使用)

1. 快速安装与启动

bash 复制代码
# 安装
npm install -g whistle

# 启动代理(默认端口8899)
w2 start

# 停止
w2 stop

# 重启
w2 restart

访问管理面板:http://127.0.0.1:8899

浏览器/系统代理地址:127.0.0.1:8899

2. HTTPS抓包:证书正确安装(高频踩坑)

Windows 致命误区

❌ 不要双击证书直接安装!双击默认导入【当前用户】certmgr.msc

Chromium(Chrome/Edge)新版策略:不信任当前用户导入的私有根证书

正确操作:

  1. 访问 http://127.0.0.1:8899/#cert 下载根证书 rootCA.cer
  2. Win+R 输入 certlm.msc(管理员权限,本地计算机存储)
  3. 受信任的根证书颁发机构 → 导入证书
  4. 完全关闭所有浏览器窗口再重启

区分两个证书管理器:

  • certmgr.msc:当前用户(容易出现「证书安装成功依然不安全」)
  • certlm.msc:本地计算机(全局生效,推荐)

Firefox 额外注意

Firefox 不读取系统证书库,需要在浏览器设置内单独导入根证书。

3. 核心界面功能说明

  1. Network:抓包列表,查看请求、响应、耗时、Headers
  2. Rules:规则配置面板,whistle核心,类似增强版hosts
  3. Values :内置资源仓库,存放JS/JSON/HTML,通过 {文件名} 在规则引用
  4. Plugins:插件市场

Values 面板作用

Values 相当于 whistle 内置云文件仓库:

  • 存放mock json、注入脚本、module脚本
  • 规则中使用 {filename} 引用,无需填写本地绝对路径
  • 修改内容后必须点击 Save 生效
  • 区别:
    • file:///xxx:读取电脑磁盘真实文件
    • {demo.js}:读取Values内部文件

4. 常用基础规则大全

规则语法:匹配url 操作://参数

匹配顺序:从上至下依次执行;精确匹配 > 通配符匹配

4.1 请求转发(代理到本地后端)

复制代码
# 将线上域名全部转发到本地 127.0.0.1:8888
test-api.example.com http://127.0.0.1:8888

# 路径匹配
test-api.example.com/admin-api/** http://127.0.0.1:8888/admin-api

4.2 修改请求头 / 删除请求头

复制代码
# 新增/覆盖请求头
example.com/** reqHeader://Authorization=Bearer token123

# 删除指定请求头(重点!解决协商缓存304)
example.com/static/** reqHeaders://!If-None-Match
example.com/static/** reqHeaders://!If-Modified-Since

4.3 修改响应头

复制代码
# 单数 resHeader:// 支持内置变量 ${randomUUID}
example.com resHeader://ETag="${randomUUID}"

# 复数 resHeaders:// JSON格式,【不支持变量插值】
example.com resHeaders://{"Cache-Control":"no-store"}

⚠️ 重要坑:resHeaders://{} 静态JSON模式不会解析 ${randomUUID},动态头必须使用多条 resHeader://

4.4 Mock 接口响应

复制代码
# 直接返回JSON字符串
example.com/api/user resBody://{"code":0,"data":{}}

# 读取Values内mock.json文件
example.com/api/user resBody://{mock.json}

# 模拟500报错
example.com/api/error statusCode://500

# 模拟网络延迟3秒
example.com/api resDelay://3000

4.5 页面注入JS脚本

复制代码
# Values内存放inject.js
example.com js://{inject.js}

5. 重点场景:本地静态文件替换(file://)

开发最常用:线上JS/WASM/CSS映射本地文件

复制代码
# Mac/Linux
example.com/static/** file:///User/xxx/statics/$1

# Windows
example.com/static/** file://D:/work/statics/$1

$1 代表通配符捕获的路径,实现完整路径一一映射

重大已知缺陷(重点)

当规则同时使用 file:// 本地文件映射时,resHeader:// 内置变量 ${randomUUID} 不会被解析!

抓包可见响应头原样输出:ETag: "${randomUUID}",无法实现动态ETag。

解决方案两种:

  1. 首选方案:删除协商缓存请求头,治本
  2. 进阶方案:使用 module://{script.js} 脚本手动构造响应头

6. Values 面板深度使用

6.1 基础引用

Values新建 mock.json

json 复制代码
{
  "code": 0,
  "msg": "mock数据"
}

Rules:

复制代码
example.com/api/list resBody://{mock.json}

6.2 module:// 脚本入口(高级能力)

Values创建 staticProxy.js,用于接管文件映射+动态修改响应头,解决file无法使用变量的问题。

javascript 复制代码
const { v4: uuidv4 } = require('uuid');
module.exports = async function(ctx) {
  // 读取本地文件
  const filePath = `/User/xxx/statics/${ctx.params[0]}`;
  await ctx.executeRule(`file://${filePath}`);
  // 动态设置ETag
  ctx.setResponseHeader('ETag', `"${uuidv4()}"`);
  ctx.setResponseHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
}

Rules调用:

复制代码
example.com/static/** module://{staticProxy.js}($1)

7. 进阶:HEAD请求引发缓存污染(真实业务痛点)

现象

浏览器先发 HEAD 请求校验资源,获取旧ETag;后续GET请求携带 If-None-Match,服务器返回304,whistle不会执行file本地文件替换,持续加载旧缓存。

根治完整规则模板

复制代码
# 静态资源映射交给脚本处理
example.com/static/** module://{staticProxy.js}($1)
# HEAD请求同样走本地文件,禁止访问源站产生缓存
example.com/static/** method:HEAD module://{staticProxy.js}($1)

# 删除协商缓存请求头,杜绝304响应
example.com/static/** reqHeaders://!If-None-Match
example.com/static/** reqHeaders://!If-Modified-Since

8. 经典疑难问题汇总

问题1:规则配置了但是不生效

排查顺序:

  1. 确认代理已经正确指向whistle 127.0.0.1:8899
  2. Network面板确认请求被whistle捕获
  3. 检查规则匹配URL、通配符语法、顺序
  4. 清除浏览器缓存,关闭所有浏览器窗口重启

问题2:证书已安装,HTTPS网站提示不安全

  1. Windows:确认导入 certlm.msc(本地计算机),不要用certmgr.msc
  2. 确认导入【根证书】,不是站点证书
  3. 关闭全部浏览器进程,不要只刷新标签页

问题3:file:// 本地文件替换,浏览器一直加载旧文件(缓存)

根因:协商缓存304,whistle不会下发本地文件。

不要只依赖修改ETag,必须删除 If-None-Match 请求头

问题4:resHeader://ETag="${randomUUID}" 原样输出不解析

原因:搭配 file:// 映射时内置变量解析失效;改用 module:// JS脚本动态设置响应头。

问题5:resHeaders://JSON 无法使用变量

官方机制:resHeaders://{...} 属于静态JSON,不支持模板变量;动态头使用多条 resHeader://key=value

9. 最佳实践模板(直接复制使用)

模板1:线上静态资源代理本地文件(防缓存完整版)

复制代码
# 静态资源代理脚本
www.example.com/static/** module://{staticProxy.js}($1)
www.example.com/static/** method:HEAD module://{staticProxy.js}($1)

# 移除协商缓存请求头,杜绝304
www.example.com/static/** reqHeaders://!If-None-Match
www.example.com/static/** reqHeaders://!If-Modified-Since

模板2:接口转发到本地服务

复制代码
# 接口代理
test-api.example.com/admin-api/** http://127.0.0.1:8888/admin-api
# 跨域头
test-api.example.com/admin-api/** resCors://*

模板3:禁用缓存通用规则

复制代码
# 全局禁用缓存
example.com/** resHeader://Cache-Control=no-cache, no-store, must-revalidate
example.com/** resHeader://Pragma=no-cache
example.com/** resHeader://Expires=0

如果你需要,我可以再补充:

  1. 配套的 staticProxy.js 完整注释版本
  2. 一份可长期保存的通用Rules预设合集
  3. 移动端抓包(手机HTTPS)完整配置章节