前言
目标接口是 http://baidu.com/getWeather?date=2026-11-04&city=beijing,要的效果是请求打到 whistle 就停住,直接吐回北京 2026-11-04 的天气 JSON。
下面每条命令和每张截图都是在本机跑出来的。中途踩了几个坑,单独记在第 6 节。
0. 环境与基本命令
- Windows
- Node.js v22.22.2
- whistle 2.10.13 不同版本的界面细节可能会有出入。
w2 的常用子命令:
| 命令 | 作用 |
|---|---|
w2 start |
后台启动,关掉终端也还在跑 |
w2 run |
前台启动,日志直接打到终端,Ctrl+C 结束 |
w2 stop |
停掉后台进程 |
w2 restart |
重启。改过配置或装过插件之后用 |
w2 status |
看进程在不在、监听哪个端口 |
w2 proxy |
把系统代理指到 whistle,w2 proxy 0 关掉 |
w2 ca |
装根证书,需要解 HTTPS 时才用 |
w2 install <插件名> |
装插件,uninstall 是卸 |
默认端口 8899,管理界面在 http://127.0.0.1:8899/。端口被占了就 w2 start -p 9000 换一个,之后 curl、浏览器代理、前端 proxy 配置里的端口都得跟着改。
启动时还有几个参数偶尔用得上:-S <目录> 换配置存储位置,-n 用户名 -w 密码 给管理界面加登录(默认没有密码,局域网里谁都能进)。
1. 安装并启动
Whistle 是个 Node.js 写的本地代理,装完就能用,不需要后端配合。
bash
npm i -g whistle # 约 10 秒
w2 --version # 2.10.13
w2 start
启动成功后终端会打印可访问地址:
csharp
[i] Whistle@2.10.13 started
[i] 1. Use your device to visit these URLs and note which one works:
http://127.0.0.1:8899/
...
[i] 2. Set your device's HTTP PROXY to the working IP & PORT(8899)
浏览器打开 http://127.0.0.1:8899/,长这样就是起来了:
左边四个入口。Network 是抓包列表,走过代理的请求都落在这儿;Rules 是规则配置,这次要动的就是它;Values 存变量和常量;最后一个是插件管理。
2. 准备模拟数据
新建一个 JSON 文件,内容就是假后端要返回的东西。放在 mock/weather.json:
json
{
"code": 0,
"message": "success",
"data": {
"city": "beijing",
"cityName": "北京",
"date": "2026-11-04",
"week": "星期三",
"weather": "晴",
"weatherCode": "100",
"temperature": {
"current": 8,
"high": 11,
"low": -2,
"unit": "℃"
},
"humidity": 34,
"wind": { "direction": "西北风", "scale": 3, "speed": 12 },
"aqi": { "value": 65, "level": "良", "pm25": 42 },
"sunrise": "06:47",
"sunset": "17:12",
"tips": "昼夜温差较大,早晚注意保暖;空气质量良,适合户外活动。",
"source": "whistle-mock",
"mock": true
}
}
存盘时注意编码选 UTF-8,否则中文会乱。后面规则里要用绝对路径引用它。
3. 配置拦截规则
3.1 打开 Rules 面板
点左侧 Rules。第一次进来顶部有一条黄色提示条,让你开启 HTTPS 抓包。这次全程走 HTTP,点 Got it 关掉就行。
面板分三块:左边是规则文件列表,自带一个 Default;右边是编辑区;顶部工具栏有 ON(全局开关)、Import、Export、Save、Create、Rename、Delete。
3.2 新建一个规则文件
点 Create,输入文件名 mock-weather,回车。左栏会多出这个文件,并且自动选中。
我没直接用 Default。实测它的启用状态不可靠,enabledCount 是 0,规则存进去也不生效。新建一个名字清楚的规则文件更稳,顺便也方便按项目区分。
3.3 写规则
在右边编辑区输入(一行一条):
bash
baidu.com/getWeather file://E:/WorkBuddy/2026-11-04-09-57-42/whistle-mock-demo/mock/weather.json
weather.mock.test/getWeather file://E:/WorkBuddy/2026-11-04-09-57-42/whistle-mock-demo/mock/weather.json
第二行是留给浏览器验证的备用域名,原因见 6.4,不需要可以删。
语法就两段:
ruby
<匹配模式> <协议>://<参数>
| 部分 | 本例取值 | 说明 |
|---|---|---|
| 匹配模式 | baidu.com/getWeather |
域名加路径。不写协议头,也不写 query |
| 协议 | file:// |
拿本地文件当响应体 |
| 参数 | E:/.../weather.json |
绝对路径,Windows 下用正斜杠 |
3.4 保存
点 Save。保存后左上角计数会变成 Enabled Rules (1)。

这步最容易漏。写完不点 Save,内容只留在浏览器内存里,代理那边一点反应都没有。
4. 验证
别急着看浏览器,先用 curl 强制走代理发一次,这样能排除掉浏览器自己的协议升级行为:
bash
curl -x http://127.0.0.1:8899 -i "http://baidu.com/getWeather?date=2026-11-04&city=beijing"
本机实测输出:
css
HTTP/1.1 200 OK
server: Whistle
content-type: application/json; charset=utf-8
content-length: 686
Connection: keep-alive
{
"code": 0,
"message": "success",
"data": {
"city": "beijing",
"cityName": "北京",
"date": "2026-11-04",
"week": "星期三",
"weather": "晴",
...
}
}
这里有三个地方能说明请求根本没出去:状态码是 200(真实 baidu 的这个路径返回 404 或 301);响应头是 server: Whistle,不是 server: bfe/...;content-type 是 application/json,whistle 按 .json 后缀自己推断的,没手动设。
4.1 抓包列表
切到 Network,列表里会出现这条请求:

看 ServerIP 列,值是 127.0.0.1。真实请求这里会是远端服务器 IP。
4.2 看命中的规则
点中这一行,右侧 Overview 面板底部有 All Rules:

里面能看到 Status Code: 200、Server IP: 127.0.0.1、Response Body: 686(和文件字节数对得上),以及 rule: baidu.com/getWeather file://...,后面标着 From File: mock-weather。
规则没生效时这一栏是空的,所以它比看浏览器靠谱。
4.3 响应原文
切到 Inspectors,请求原文和响应原文都在,连响应头带完整 JSON:

4.4 浏览器
把浏览器代理指到 127.0.0.1:8899,直接访问接口地址,页面会把模拟的 JSON 渲染出来:

截图里访问的是 http://weather.mock.test/getWeather,不是 baidu.com。因为 baidu.com 在 Chrome 的 HSTS 预载列表里,浏览器会强行把它升级成 https,纯 HTTP 规则就命中不了。绕法见 6.4。
5. 让本地前端项目走 whistle
规则配好了,但前端 dev server 不走代理的话,请求还是能绕过去直达后端。两种接法。
方案 A:dev server 转发到 whistle
以 Vite 为例:
js
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8899',
changeOrigin: false, // 关键,别让 Host 被改写
},
},
},
});
changeOrigin: false 这个别改。它保证 whistle 收到的 Host 还是 localhost:5173,你写的规则才能和浏览器地址栏里的 URL 对上。设成 true 之后 Host 变成 127.0.0.1:8899,规则全线失配。
方案 B:浏览器或系统代理
bash
w2 proxy # 一键设置系统代理
w2 proxy "127.0.0.1:8899" # 指定地址
w2 proxy 0 # 关闭系统代理
可以再配个 SwitchyOmega 或 ZeroOmega,只代理目标域名。全局代理的话你所有流量都会灌进 whistle,抓包列表很快就没法看了。
6. 踩过的坑
| # | 现象 | 根因 | 解法 |
|---|---|---|---|
| 1 | w2 start 显示 started,但 w2 status 说没实例,8899 也没监听 |
守护进程被回收了 | 改用 w2 run 前台跑,或者换普通终端执行 w2 start |
| 2 | curl http://127.0.0.1:8899 返回 502 upstream connect failed |
系统里有 HTTP_PROXY 环境变量,请求被转给了别的代理 |
加 --noproxy '*',或 unset http_proxy https_proxy |
| 3 | 规则写成 baidu.com/getWeather* 反而匹配不上 |
2.10.13 下路径尾部加 * 会让 query 部分匹配失败。不加通配符时,路径规则本来就会忽略 query |
直接写 域名/路径,别加 * |
| 4 | curl 里生效,浏览器访问却是真实 404 | baidu.com 被浏览器按 HSTS 升级成 https,whistle 只做隧道透传不解密,HTTP 规则命中不了 |
换非 HSTS 域名验证(weather.mock.test);或者开启 Enable HTTPS 并装 whistle 根证书 |
| 5 | 规则存了,init 接口也读到了,请求就是不生效 |
规则文件处于未启用状态,Enabled Rules (0) |
新建自己的规则文件并 Save,确认左上角计数 ≥ 1 |
| 6 | 找不到规则存哪 | 2.10.x 把数据目录挪到了 .WhistleAppData |
C:\Users\<用户名>\.WhistleAppData\.whistle\rules\files\,文件形如 0.mock-weather |
| 7 | 前端 fetch 报跨域 |
mock 响应默认不带 CORS 头 | 规则后面追加 resHeaders://{"access-control-allow-origin":"*"} |
第 4 条还有个前因:whistle 自己也会把已知 HSTS 站点的 HTTP 请求 307 到 HTTPS。所以用 curl 验证永远最省事,它不会自作聪明改协议。
7. 切回真实后端
| 目的 | 操作 |
|---|---|
| 临时停用某条规则 | 行首加 # 注释掉,Save |
| 全局停用 | 点 Rules 工具栏的 ON 切成 OFF |
| 完全不走代理 | w2 proxy 0,前端删掉 server.proxy |
| 停掉 whistle | w2 stop |
8. 速查
bash
# 安装与启动
npm i -g whistle
w2 start # 或 w2 run 前台调试
w2 ca # 要抓 HTTPS 时才装根证书
w2 proxy # 设置系统代理
w2 proxy 0 # 关闭系统代理
w2 stop
# 验证,记得绕开系统代理变量
curl --noproxy '*' -x http://127.0.0.1:8899 "http://目标域名/接口路径?参数"
规则写法:
perl
# 静态文件
域名/路径 file:///绝对路径/xxx.json
# 内联响应体 + 状态码 + 延迟
域名/路径 resBody://{变量名} statusCode://500 reqDelay://800
域名/路径 resType://json resBody://(直接写内容)
# 只改响应头,解决跨域
域名/路径 resHeaders://{"access-control-allow-origin":"*"}
规则到底有没有命中,看两处就够:curl 返回的 server: Whistle,以及 Overview 面板底部的 All Rules。