当前位置: 首页 > news >正文

Shermie-proxy:基于Node.js的脚本化HTTP/HTTPS代理调试工具实战指南

1. 项目概述与核心价值

最近在折腾一些本地开发环境下的网络请求调试和抓包,发现一个挺有意思的开源项目kxg3030/shermie-proxy。这本质上是一个基于 Node.js 实现的 HTTP/HTTPS 代理服务器,但它的定位非常清晰:专为开发者本地调试和网络请求分析而生。如果你经常需要查看前端应用发出的请求详情、分析 API 接口数据、模拟慢速网络,或者对手机 App 的请求进行抓包,但又觉得像 Charles、Fiddler 这类图形化工具太重,或者需要一些自动化集成,那么这个轻量级的命令行代理工具很可能就是你的菜。

简单来说,Shermie-proxy 就像一个“透明中间人”,它在你本地启动一个代理服务,然后将你电脑或手机的网络流量引导到这个服务上。它不仅能记录下所有经过的 HTTP/HTTPS 请求和响应的详细信息(包括头部、Body 等),还能让你动态地修改这些请求和响应,或者模拟各种网络条件。它的核心优势在于“脚本化”和“可编程”。所有的抓包逻辑、修改规则都可以通过 JavaScript 代码来定义和扩展,这为自动化测试、定制化调试场景打开了大门。对于前端开发者、后端 API 提供者、移动端开发,甚至是测试工程师,掌握这样一个工具,能极大地提升定位网络问题的效率。

2. 核心架构与工作原理拆解

2.1 代理服务的基本模型

要理解 Shermie-proxy,首先得明白一个 HTTP/HTTPS 代理是如何工作的。想象一下邮局系统:你的信件(网络请求)原本是直接寄给收件人(目标服务器)。现在你设置了一个代理,就相当于你把所有信件都先交给一个“代理邮局”(Shermie-proxy),由它来检查、记录,然后再帮你寄出去,并将回信(服务器响应)同样经过它检查后交还给你。

在技术层面,Shermie-proxy 主要处理两种协议:

  1. HTTP 代理:相对简单。客户端(如浏览器)明确知道代理的存在,会直接向代理服务器发送完整的 HTTP 请求(包含Host头等信息)。代理服务器解析这个请求,然后代表客户端向目标服务器发起新的连接,获取响应后再返回给客户端。
  2. HTTPS 代理:这是关键,也是难点。HTTPS 要求端到端加密,代理无法直接看到明文内容。这里用到了“中间人(MITM)”技术。当客户端向代理发起CONNECT请求(例如CONNECT github.com:443 HTTP/1.1)时,代理会与目标服务器建立 TCP 连接。一旦隧道建立,客户端和服务器会开始 TLS 握手。此时,代理需要“介入”:它动态生成一个针对目标域名的假证书,并与客户端完成 TLS 握手;同时,它自己再与目标服务器进行真正的 TLS 握手。这样,代理就拥有了两端的加密会话密钥,可以对流量进行解密、记录和再加密,实现了对 HTTPS 流量的透明解析。

注意:正因为使用了 MITM 技术,你必须在客户端(浏览器、手机)上安装并信任 Shermie-proxy 自己生成的根证书(CA Certificate)。否则,客户端会检测到证书不匹配(由代理签发的假证书),从而中断连接并报安全错误。这是所有 HTTPS 抓包工具的通用原理,并非本项目独有。

2.2 Shermie-proxy 的模块化设计

Shermie-proxy 的代码结构体现了清晰的模块化思想,这使得它易于理解和扩展。其核心通常包含以下几个部分:

  1. 代理服务器核心:负责监听端口,接受客户端的代理连接请求。它根据协议(HTTP 或 HTTPS)将连接路由到不同的处理模块。
  2. 请求/响应拦截器:这是业务逻辑的核心。它提供了生命周期钩子(Hooks),允许开发者注入自定义的 JavaScript 代码。常见的钩子包括:
    • onRequest:在代理将请求转发给目标服务器之前触发。你可以在这里修改请求的 URL、方法、头部、或 Body。
    • onResponse:在代理收到目标服务器的响应之后,返回给客户端之前触发。你可以在这里修改响应的状态码、头部、或 Body。
    • onError:在请求过程中发生错误时触发。
  3. 证书管理模块:负责生成和管理用于 HTTPS 中间人攻击的根证书以及各个域名的动态叶证书。它需要处理证书的创建、缓存、以及提供给客户端下载根证书的接口。
  4. 日志与存储模块:负责将拦截到的请求和响应信息以结构化的方式(如 JSON)输出到控制台,或者存储到文件、数据库中,便于后续分析。
  5. Web UI 管理界面(可选):一些高级版本或社区插件可能会提供一个简单的 Web 界面,用于实时查看请求列表、过滤搜索、重放请求等,这比单纯看命令行日志更友好。

这种设计模式使得 Shermie-proxy 不仅仅是一个抓包工具,更是一个可编程的代理网关框架。你可以基于它快速构建出符合自己特定需求的调试工具。

3. 从零开始的环境搭建与配置

3.1 基础环境准备

首先,你需要一个 Node.js 运行环境。建议安装最新的 LTS(长期支持)版本,这能保证最好的兼容性和稳定性。你可以从 Node.js 官网下载安装包,或者使用nvm(Node Version Manager)这类工具来管理多个版本。

安装完成后,打开终端,通过以下命令验证:

node --version npm --version

正常显示版本号即表示环境就绪。

接下来,获取 Shermie-proxy 的代码。由于它是一个开源项目,通常你需要克隆其 Git 仓库:

git clone https://github.com/kxg3030/shermie-proxy.git cd shermie-proxy

然后安装项目依赖:

npm install

如果项目提供了全局命令行工具,你可能还需要进行全局安装(通常使用npm install -g .或根据项目文档说明)。

3.2 核心配置详解

Shermie-proxy 的启动和行为通常由一个配置文件(如config.js)或启动参数来控制。理解这些配置项是高效使用它的关键。

一个典型的配置文件可能包含以下内容:

module.exports = { // 代理服务器监听的端口 port: 8899, // 是否启用HTTPS代理(即支持CONNECT方法) ssl: true, // 证书相关配置 cert: { // 根证书和私钥的存放路径,如果不存在会自动生成 caCertPath: './cert/ca.crt', caKeyPath: './cert/ca.key.pem', }, // 请求/响应的拦截规则脚本路径 rule: './myRule.js', // 是否静默运行(减少控制台输出) silent: false, // 是否记录请求体/响应体(可能影响性能,对于大文件慎用) recordBody: true, // 排除某些域名或IP不走代理(可用于绕过对本地服务或某些敏感地址的代理) exclude: ['127.0.0.1', 'localhost', '192.168.1.100'], // WebSocket代理支持 websocket: true, };

关键配置解析:

  • port:选择一个不常用的高端口号(如 8899, 8888),避免与现有服务冲突。
  • sslcert:这是 HTTPS 抓包的开关和钥匙。首次运行且ssltrue时,工具会在cert指定路径生成一对自签名的根证书(CA)。你必须将生成的ca.crt文件导入到你的操作系统或浏览器的“受信任的根证书颁发机构”中。这是让浏览器信任代理所签发假证书的唯一方法。
  • rule:这是 Shermie-proxy 的灵魂。它指向一个你自己编写的 JS 文件,在这个文件里你可以定义所有的拦截和修改逻辑。
  • exclude:非常实用的配置。比如你本地跑了一个后端服务在localhost:3000,你肯定不希望前端请求这个地址的流量也被代理抓取和修改,否则可能导致循环代理或连接问题。把它加入排除列表即可。

3.3 客户端代理设置

启动 Shermie-proxy 服务后,它只是一个在本地127.0.0.1:8899监听的服务。你需要告诉你的应用程序将流量发送给它。

  • 全局系统代理:在 macOS 或 Windows 的网络设置中,手动设置 HTTP 和 HTTPS 代理为127.0.0.1:8899。这种方法对所有应用生效,但可能会影响一些不使用系统代理的应用(如命令行 curl 的某些情况)。
  • 浏览器代理:使用浏览器插件(如 SwitchyOmega)进行配置,可以灵活地针对不同网址模式切换代理或直连,不影响其他系统应用。
  • 命令行工具:为curlwget等命令设置代理环境变量:
    export http_proxy=http://127.0.0.1:8899 export https_proxy=http://127.0.0.1:8899 # 然后运行的curl命令就会经过代理 curl https://api.example.com
  • 移动端抓包:确保手机和电脑在同一局域网。在手机的 Wi-Fi 设置中,找到当前网络,配置代理为“手动”,填入电脑的局域网 IP 地址和代理端口(如192.168.1.10:8899)。同时,你还需要在手机浏览器中访问http://<电脑IP>:<代理端口>/(例如http://192.168.1.10:8899),以下载并安装根证书(通常 Shermie-proxy 会提供这个下载入口)。安装后,记得在手机的证书设置里将其标记为“受信任”。

实操心得:我强烈推荐使用“浏览器插件 + 规则配置”的方式。将日常浏览的流量交给系统代理或浏览器默认代理,而只将需要调试的特定域名(如你的开发环境dev.your-app.com)的流量定向到 Shermie-proxy。这样既保证了调试的针对性,又不会干扰正常的网页浏览和其他网络活动。

4. 核心功能实战:编写拦截规则脚本

rule配置项指向的 JS 文件,是你发挥创造力的地方。这个文件需要导出一个包含特定生命周期方法的对象。我们通过几个经典场景来学习如何编写规则。

4.1 场景一:记录与诊断 API 请求

假设你在调试一个前端应用,想看看它到底向后端发了什么请求,以及返回了什么数据。

// myRule.js - 基础日志记录 module.exports = { /** * 请求即将发出时触发 * @param {Object} requestInfo 请求信息对象 * @param {Function} callback 回调函数,用于修改请求或直接响应 */ onRequest: async (requestInfo, callback) => { const { url, method, headers, body } = requestInfo; console.log(`[请求] ${method} ${url}`); console.log('请求头:', JSON.stringify(headers, null, 2)); if (body) { // 注意:body可能是Buffer、字符串或JSON对象,需谨慎处理 console.log('请求体:', body.toString('utf8').substring(0, 500)); // 只打印前500字符 } // 必须调用callback,不传参数表示继续正常转发请求 callback(); }, /** * 收到服务器响应时触发 * @param {Object} responseInfo 响应信息对象 * @param {Function} callback 回调函数,用于修改响应 */ onResponse: async (responseInfo, callback) => { const { statusCode, headers, body, request } = responseInfo; console.log(`[响应] ${request.method} ${request.url} -> ${statusCode}`); console.log('响应头:', JSON.stringify(headers, null, 2)); if (body && headers['content-type']?.includes('application/json')) { try { const jsonBody = JSON.parse(body.toString('utf8')); console.log('响应体(JSON):', JSON.stringify(jsonBody, null, 2)); } catch (e) { console.log('响应体(原始):', body.toString('utf8').substring(0, 1000)); } } // 继续将(可能修改后的)响应返回给客户端 callback(); }, };

这个脚本会将每个请求和响应的关键信息打印到控制台。对于 JSON 格式的响应,它会进行美化输出,便于阅读。

4.2 场景二:修改请求与响应(Mock 数据)

这是本地开发中最常用的功能之一:修改 API 返回的数据,或者改变请求参数。

// myRule.js - 修改请求与Mock数据 module.exports = { onRequest: async (requestInfo, callback) => { const { url, method } = requestInfo; // 场景1:修改请求路径(重定向API) if (url.includes('/api/old-endpoint')) { requestInfo.url = requestInfo.url.replace('/api/old-endpoint', '/api/new-endpoint'); console.log(`重写请求路径至: ${requestInfo.url}`); } // 场景2:修改请求头(例如添加认证Token) if (url.includes('/api/secure')) { requestInfo.headers['Authorization'] = 'Bearer your-mock-token-here'; } // 场景3:修改POST请求的JSON Body if (method === 'POST' && url.includes('/api/submit') && requestInfo.body) { try { const bodyObj = JSON.parse(requestInfo.body.toString('utf8')); bodyObj.timestamp = Date.now(); // 注入时间戳 bodyObj.env = 'development'; // 注入环境标识 requestInfo.body = Buffer.from(JSON.stringify(bodyObj), 'utf8'); // 记得更新Content-Length头部,但Shermie-proxy可能会自动处理 // requestInfo.headers['content-length'] = Buffer.byteLength(requestInfo.body); } catch (e) { /* 忽略非JSON Body */ } } callback(); }, onResponse: async (responseInfo, callback) => { const { statusCode, headers, body, request } = responseInfo; // 场景1:Mock特定接口的响应数据 if (request.url.includes('/api/user/profile')) { // 直接返回一个自定义的响应,不向真实服务器发送请求 // 注意:这需要在onRequest里拦截并调用callback({ response: ... }),更常见的做法是在onRequest里拦截 // 但这里演示在onResponse里替换真实服务器的响应 const mockData = { code: 0, data: { userId: 1001, name: 'Mock User', avatar: 'https://placeholder.com/avatar.jpg' }, message: 'success (mocked)' }; responseInfo.statusCode = 200; responseInfo.headers['content-type'] = 'application/json; charset=utf-8'; responseInfo.body = Buffer.from(JSON.stringify(mockData), 'utf8'); // 删除可能存在的压缩头,因为我们提供了新的明文Body delete responseInfo.headers['content-encoding']; delete responseInfo.headers['content-length']; // 工具会重新计算 console.log(`已Mock接口: ${request.url}`); } // 场景2:修改真实服务器的响应(例如,给所有JSON响应加一个字段) if (statusCode === 200 && headers['content-type']?.includes('application/json') && body) { try { const jsonBody = JSON.parse(body.toString('utf8')); // 避免循环修改或破坏结构 if (jsonBody && typeof jsonBody === 'object' && !jsonBody._debug) { jsonBody._debug = { proxyModified: true, time: new Date().toISOString() }; responseInfo.body = Buffer.from(JSON.stringify(jsonBody), 'utf8'); // 同样需要清理压缩和长度头 delete responseInfo.headers['content-encoding']; delete responseInfo.headers['content-length']; } } catch (e) { /* 忽略解析错误 */ } } // 场景3:模拟服务器错误或延迟 if (request.url.includes('/api/unstable')) { // 模拟5秒延迟 await new Promise(resolve => setTimeout(resolve, 5000)); // 或者模拟500错误 // responseInfo.statusCode = 500; // responseInfo.body = Buffer.from('Internal Server Error (Simulated)', 'utf8'); } callback(); }, };

4.3 场景三:请求阻断与重定向

// myRule.js - 阻断与重定向 module.exports = { onRequest: async (requestInfo, callback) => { const { url } = requestInfo; // 场景1:完全阻断对某个广告或统计域名的请求(提升页面加载速度) const blockList = ['ads.doubleclick.net', 'www.google-analytics.com', 'sdk.analytics.xxx']; if (blockList.some(domain => url.includes(domain))) { console.log(`阻断请求: ${url}`); // 直接返回一个空的成功响应,模拟请求被屏蔽 callback({ response: { statusCode: 200, headers: { 'content-type': 'text/plain' }, body: 'Blocked by proxy' } }); return; // 重要:直接返回,不再执行后续逻辑和转发 } // 场景2:将请求重定向到本地开发服务器 if (url.includes('production-api.example.com')) { const newUrl = url.replace('production-api.example.com', 'localhost:3000'); requestInfo.url = newUrl; console.log(`请求重定向至本地: ${newUrl}`); // 可能需要修改Host头,否则本地服务器可能无法识别虚拟主机 requestInfo.headers['host'] = 'localhost:3000'; } // 场景3:根据请求特征返回自定义响应(Mock) if (url.endsWith('/api/config') && requestInfo.method === 'GET') { callback({ response: { statusCode: 200, headers: { 'content-type': 'application/json' }, body: JSON.stringify({ theme: 'dark', language: 'zh-CN', mock: true }) } }); return; } // 如果不是上述情况,正常转发 callback(); } };

5. 高级应用与性能调优

5.1 处理 WebSocket 连接

现代应用大量使用 WebSocket 进行实时通信。Shermie-proxy 通过配置websocket: true来支持代理 WebSocket 连接。在规则脚本中,你可以监听onWebSocketFrame等事件来拦截和修改 WebSocket 收发的数据帧。这对于调试聊天应用、实时仪表盘等场景非常有用。处理 WebSocket 时需注意,数据帧可能是文本也可能是二进制,需要根据opcode进行区分处理。

5.2 性能考量与优化建议

代理工具毕竟增加了一层网络跳转和处理,可能会对性能产生轻微影响,尤其是在记录完整请求/响应体时。以下是一些优化建议:

  1. 选择性记录:在规则脚本中,通过判断 URL、域名或内容类型,只记录你关心的请求。对于图片、视频、大型文件等请求,跳过body的记录和打印。
    onResponse: async (responseInfo, callback) => { const contentType = responseInfo.headers['content-type'] || ''; if (contentType.startsWith('image/') || contentType.startsWith('video/') || contentType.includes('octet-stream')) { // 跳过媒体和二进制流体的详细记录 callback(); return; } // ... 其他处理逻辑 }
  2. 使用静默模式:生产调试规则时,可以开启配置中的silent: true,关闭控制台默认的请求日志,只输出你自己在规则脚本中定制的关键信息。
  3. 合理设置排除列表:将本地局域网地址、已知的大文件下载域名等加入exclude配置,让它们直连,减轻代理负担。
  4. 避免同步阻塞操作:在onRequestonResponse钩子中,避免执行耗时的同步操作(如大型文件读写、复杂计算)。如果必须执行,确保使用异步方式,并尽快调用callback(),以免阻塞代理管道。

5.3 集成到自动化工作流

Shermie-proxy 的脚本化特性使其易于与自动化测试框架(如 Jest, Mocha, Playwright, Cypress)集成。你可以在测试套件启动前,以编程方式启动 Shermie-proxy 并加载特定的 Mock 规则,从而确保测试环境拥有稳定、可控的网络响应。

例如,使用child_process模块在 Node.js 测试脚本中启动代理:

const { spawn } = require('child_process'); const path = require('path'); beforeAll(async () => { // 启动代理服务器,指定配置文件 const proxyProcess = spawn('node', [path.resolve('./node_modules/.bin/shermie-proxy'), '-c', './testProxyConfig.js']); // 等待代理服务就绪 await new Promise(resolve => setTimeout(resolve, 2000)); global.__PROXY_PROCESS__ = proxyProcess; }); afterAll(() => { if (global.__PROXY_PROCESS__) { global.__PROXY_PROCESS__.kill(); } });

这样,你的端到端(E2E)测试或接口测试就能在一个被精确控制网络行为的沙箱中运行。

6. 常见问题排查与实战技巧

6.1 HTTPS 抓包失败或证书错误

这是最常见的问题。

  • 症状:浏览器访问 HTTPS 网站显示“您的连接不是私密连接”、“NET::ERR_CERT_AUTHORITY_INVALID”。
  • 原因:客户端(浏览器/手机)没有安装或信任 Shermie-proxy 生成的根证书。
  • 解决
    1. 启动 Shermie-proxy(SSL 启用)后,访问http://127.0.0.1:<端口>(如http://127.0.0.1:8899),页面通常会提供根证书下载链接。
    2. 下载ca.crt文件。
    3. 导入系统或浏览器
      • macOS:双击.crt文件,打开“钥匙串访问”,找到导入的证书,右键点击 -> “显示简介” -> “信任” -> 将“使用此证书时”设置为“始终信任”。
      • Windows:双击.crt文件 -> “安装证书” -> “当前用户” -> “将所有的证书都放入下列存储” -> “浏览” -> 选择“受信任的根证书颁发机构”。
      • 浏览器:有些浏览器(如 Chrome)使用系统证书库,有些(如 Firefox)有独立的证书管理器,需要在浏览器设置中手动导入并信任。
    4. 手机端:确保通过http://<电脑IP>:端口下载并安装了证书,且在系统设置中将其设置为“受信任的凭据”(位置因安卓版本而异)。

实操心得:有时候即使安装了证书,某些 App(尤其是安卓高版本或 iOS)仍可能抓不到包,这是因为它们使用了“证书锁定(Certificate Pinning)”技术。这种情况下,代理工具就无能为力了,除非能修改 App 本身。对于自己开发的 App,在开发阶段应关闭证书锁定。

6.2 代理设置后无法上网

  • 症状:设置了代理,但浏览器无法访问任何网站,或只有部分网站无法访问。
  • 排查
    1. 检查代理服务是否运行:确认 Shermie-proxy 进程是否在运行,监听端口是否正确。
    2. 检查排除列表:是否错误地将需要访问的地址(如网关192.168.1.1)加入了exclude列表?或者,是否应该将一些地址加入排除列表却没加?
    3. 检查规则脚本:你的onRequest回调函数是否在所有分支路径上都正确调用了callback()?如果某个条件分支下没有调用callback(),请求会被挂起。
    4. 检查防火墙:本地防火墙是否阻止了代理服务(Node.js 进程)访问网络?

6.3 请求体或响应体显示为乱码或空白

  • 症状:抓到的请求/响应 Body 是乱码、[object Object]或者直接是空白。
  • 原因与解决
    1. 压缩内容:服务器返回的响应可能经过 Gzip、Deflate 压缩。Shermie-proxy 默认可能会解压,但需确认配置。在规则脚本中,如果修改了响应体,需要删除headers['content-encoding'],否则浏览器无法解压被修改过的压缩数据。
    2. 二进制内容:对于图片、PDF 等二进制数据,直接toString()会乱码。在记录前应先判断content-type,对于二进制类型,可以记录为[Binary Data - size: xxx bytes]
    3. Buffer 处理requestInfo.bodyresponseInfo.body通常是 Buffer 对象。需要使用body.toString('utf8')来转换为字符串(针对文本)。转换前最好用Buffer.isBuffer(body)判断一下。
    4. 大文件截断:为了性能和控制台可读性,你的规则脚本可能只打印了 Body 的前一部分字符。

6.4 性能瓶颈与内存占用

  • 症状:代理运行一段时间后变慢,或者 Node.js 进程内存占用越来越高。
  • 优化
    1. 关闭不必要的日志:使用silent模式,并在规则脚本中避免对每个请求都执行console.log,尤其是打印完整的 Body。
    2. 限制 Body 记录大小:在配置中或规则脚本里,对于过大的请求/响应体,跳过记录或只记录元数据。
    3. 检查规则脚本内存泄漏:确保在规则脚本中没有意外地保存了对请求/响应对象的大型引用(例如将其推入一个全局数组且从不清理)。这会导致垃圾回收无法进行,内存持续增长。
    4. 定期重启:对于长期运行的调试任务,可以设置定时任务或手动定期重启代理服务。

6.5 与其他工具冲突

  • 症状:端口被占用,或者代理行为不符合预期。
  • 解决
    1. 端口占用:换一个端口,或关闭占用该端口的其他程序(如其他抓包工具、开发服务器)。
    2. 多层代理:如果你身处公司网络,已经有一层网络代理,那么你需要处理代理链。一种方法是在 Shermie-proxy 的规则中,对于需要访问外网的请求,将其转发到上层代理。这需要更复杂的网络配置。
    3. VPN 干扰:某些 VPN 软件会修改系统网络栈,可能与本地代理冲突。尝试暂时断开 VPN。

7. 安全使用须知与最佳实践

使用 MITM 代理工具 inherently 具有安全敏感性,请务必遵循以下准则:

  1. 仅用于合法调试:仅在你自己拥有完全控制权的设备、网络和应用程序上使用,用于开发、测试、调试目的。绝对不要用于拦截他人的网络通信,这是非法的。
  2. 保护你的根证书:生成的ca.crtca.key.pem文件是安全关键资产。任何人拿到它们都可以对你信任该 CA 的网络流量进行解密。切勿将其提交到公开的代码仓库或通过网络传输。建议在.gitignore中忽略cert/目录。
  3. 及时移除证书:当结束调试后,应从系统和浏览器中移除你安装的自签名根证书,以免留下安全漏洞。
  4. 隔离使用环境:最好在虚拟机、容器或专用的开发机器上使用此类工具,与日常办公、金融操作的环境隔离开。
  5. 审查规则脚本:从网络上下载的规则脚本可能包含恶意代码。运行前务必仔细审查,尤其是涉及网络请求转发、数据上报等操作的代码。

我个人在长期使用这类工具的过程中,最大的体会是:明确边界,善用其长。Shermie-proxy 这类脚本化代理,其威力在于灵活性和自动化能力,适合深度、定制化的调试场景。对于简单的查看请求响应,图形化工具可能更直观;但对于需要集成到 CI/CD、自动化测试、或者复杂请求/响应修改链路的场景,它就是无可替代的利器。开始时可能会在证书配置、脚本编写上踩些坑,但一旦跑通,它将成为你开发工具箱里一件非常趁手的“手术刀”,能精准地解剖和调试网络层面的任何问题。最后一个小技巧:把你的常用规则脚本模块化,比如logRule.jsmockApiRule.jsblockAdsRule.js,然后在主配置中动态引入,这样就能像搭积木一样快速组合出当前调试任务所需的代理环境。

http://www.jsqmd.com/news/839328/

相关文章:

  • NotebookLM在博物馆学中的应用突破(2024国家一级馆实测数据首发)
  • 电赛小车结构避坑指南:从齿轮齿条到剪叉式,我们为什么最终选了舵机+剪叉方案?
  • 手把手教你学Simulink--电动物流车预充电路控制及主继电器粘连检测电机负载仿真
  • 佛山二手名表回收避坑攻略,内行教你避开黑心套路 - 奢侈品回收测评
  • 高效Vue代码差异对比插件:v-code-diff完整使用指南
  • 尚硅谷 Nginx 教程(亿级流量 Nginx 架构设计),基本使用,笔记 6-42
  • 5分钟打造专业直播间:OBS智能背景移除插件完全指南
  • DLSS Swapper:一键切换游戏DLSS版本,让NVIDIA显卡性能起飞
  • nvm-windows深度实战:Windows平台Node.js版本管理的系统化解决方案
  • 质粒测序数据自动化QC与比对分析:从Sanger测序到变异检测全流程
  • 解码FTP传输乱码:从Windows10 FTP 451错误看Unicode与多字节编码的世纪和解
  • 2026年石锅拌饭加盟厂家推荐:菏泽万华餐饮管理有限公司,石锅海鲜/石锅鱿鱼/石锅鸡/石锅豆腐/石锅菜/石锅鱼精选 - 品牌推荐官
  • 050二叉树中的最大路径和
  • 为Grok等大模型构建高效网页内容抓取与结构化提取工具
  • 重庆川岳机电设备:高新区性价比高的吊装搬运怎么联系 - LYL仔仔
  • PyInstaller Extractor终极指南:5分钟学会提取可执行文件源码
  • 从零构建私有数字保险库:硬件选型、加密策略与实战部署
  • FPGA深度学习加速器设计与能效优化实践
  • 紧急通知:2024秋季学期起,牛津/北大文学系已将NotebookLM列为必修研究工具——你还在手动做人物关系表?
  • 为什么永辉超市卡常被闲置?3个关键原因分析及回收技巧 - 团团收购物卡回收
  • 从SIFT到SURF:为什么‘加速’和‘稳健’对移动端图像识别App如此重要?
  • 虚幻引擎自定义网络协议开发指南:从原理到实践
  • 昆山打官司胜诉率高的律师服务选择要点分析 - 品牌排行榜
  • 5分钟搞定!Postman便携版:你的API测试随身工具箱 [特殊字符]
  • 【终端窗口掌控术】Linux resize命令:从基础调整到自动化脚本的进阶指南
  • 3个核心技巧:用League Akari成为英雄联盟高效玩家
  • 3步掌握ffmpeg-static:从零部署到生产环境完全指南
  • 终极指南:如何快速在Windows上安装Android应用?告别模拟器的完整解决方案
  • 通过Taotoken用量看板分析CRM网站AI功能的使用峰值与规律
  • 3分钟学会Win11Debloat:彻底清理Windows预装应用和隐私设置