在调试网络问题时,有一个明显的分界点:
- 只查看请求和响应
- 主动修改请求和响应,观察程序行为变化
当调试目标落在第二种情况,抓包工具是否支持请求 / 响应拦截器,就成了决定效率的关键因素。
确认拦截位置,是在发送前,还是返回后
在动手写拦截逻辑之前,我会先确认一个我要干预的是请求阶段,还是响应阶段。
举两个常见的调试目标:
- 请求发出前,替换 Header、参数或 URL
- 响应返回后,修改字段、状态码或数据结构
这一步会直接决定拦截器是否需要同时处理 request 和 response。
代理型工具中的拦截能力
在 Charles、Fiddler 这类代理工具中,拦截通常以规则或断点形式存在。
以代理断点为例:
- 设置 URL 匹配规则
- 请求命中后暂停
- 手动修改请求内容再继续发送
这种方式适合临时验证接口行为,但当需要批量、可复现的修改逻辑时,手动断点的效率会迅速下降。
脚本化拦截的必要性
当调试需求变成:
- 每次请求都要自动改 Header
- 特定接口返回值需要统一替换
- 多个字段需要条件判断后再修改
就需要使用脚本型拦截器。
这类拦截器有几个明确特征:
- 请求与响应自动进入拦截逻辑
- 修改过程可重复
- 行为可通过日志验证
在抓包大师中启用请求 / 响应拦截器
在需要对 iOS App 网络数据做深度调试时,我会使用 抓包大师(Sniff Master) 的拦截器功能。
打开拦截器日志界面
- 进入代理抓包界面
- 双击右侧插件形状的拦截器图标
- 打开拦截器日志窗口
在这个界面中,可以直接看到:
- 拦截功能是否启用
- 当前拦截日志是否产生
日志是否出现,是判断拦截是否生效的直接依据。
进入拦截器编辑器,理解代码结构
在拦截器日志界面中,点击「编辑拦截器」,会进入代码编辑窗口。
拦截器使用 JavaScript 编写,整体结构固定,由三个函数组成:
function handleRequest(request) {
return request
}
function handleResponse(response) {
return response
}
function filterUrl() {
return []
}
这段代码本身不会拦截任何请求,它的作用是提供一个完整但空行为的框架。
拦截范围由 filterUrl 决定
filterUrl() 返回的是一个 URL 规则数组。
例如:
function filterUrl() {
return ["https://api.example.com/*"]
}
只有命中该规则的请求,才会进入 handleRequest 和 handleResponse。
如果这里返回空数组,拦截器不会对任何请求生效。
在请求阶段修改数据
handleRequest(request) 接收到的是即将发送给服务器的数据。
request 对象中可直接访问:
request.URLrequest.Headerrequest.Bodyrequest.IsBase64Body
例如,替换请求 Header 中的某个字段:
function handleRequest(request) {
request.Header["token"] = "debug-token"
return request
}
修改完成后返回 request,对服务器而言,这就是最终收到的请求。
在响应阶段修改返回结果
handleResponse(response) 接收的是服务器返回的数据。
response 中除了 URL、Header、Body,还包含:
response.StatusCode
例如,修改响应内容以验证客户端逻辑:
function handleResponse(response) {
response.Body = '{"code":0,"data":"mock"}'
return response
}
客户端接收到的就是被替换后的响应内容。
拦截器是否生效,用日志验证
在调试过程中,我会在拦截函数中加入日志输出:
console.log("拦截请求:" + request.URL);
如果日志窗口中出现对应输出,说明:
- URL 匹配成功
- 拦截逻辑被执行
- 修改行为真实生效
这是比“界面有没有变化”更可靠的判断方式。
多工具协同的使用方式
在实际工程中,拦截器很少单独使用:
- 用 Wireshark 判断是否建立连接
- 用代理工具确认请求路径
- 用 抓包大师(Sniff Master)拦截并修改数据
每个工具解决的都是不同层级的问题,拦截器负责的是数据层面的验证和控制
参考链接:https://www.sniffmaster.net/tutorial/zh/6/6.html