开源 API 调试工具选型之颠覆Postman

一、为什么要离开 Postman?

Postman 作为 API 调试领域的标杆,曾是每个后端开发者的必备工具。但近两年情况急转直下:

1.1 强制登录与数据云端化

Postman v10 之后强制要求登录账号,"离线模式"被大幅削弱——本地集合默认同步到 Postman 云端。对公司内网开发、安全合规要求严苛的团队而言,这是致命一击。对个人开发者而言,近因网络扰动导致无法登录不能使用的情况时有发生。

1.2 性能越来越臃肿

一个 Electron 应用动辄吃掉 1-2GB 内存,启动时间超过 10 秒。对比之下 VS Code 都比它轻快。

1.3 功能受限与付费加压

免费版集合协作人数降至 3 人,Scratch Pad 模式功能残缺(不支持环境变量、脚本等)。想好好用?请付 $12/人/月。

这些变化让我开始寻找替代方案。


二、候选工具调研

2.1 候选清单

工具定位仓库地址
Bruno本地优先、纯文本、Git 友好github.com/usebruno/bruno
HoppscotchWeb 端优先的轻量客户端github.com/hoppscotch/hoppscotch
Insomnia老牌 REST/GraphQL 客户端github.com/Kong/insomnia
httpYacVSCode 插件,纯文本定义github.com/AnWeber/httpyac
VS Code REST Client编辑器内 .http 文件内置扩展

2.2 评估维度与对比

维度BrunoHoppscotchInsomniahttpYac
本地优先✅ 纯本地⚠️ 默认 Web⚠️ 需账号✅ VSCode 内
Git 友好✅ 纯文本 Bru 格式❌ JSON 导出❌ JSON 导出.http 文件
离线可用✅ 完全离线✅ 自部署可行⚠️ 功能受限
脚本支持✅ JS (Bruno 语法)✅ JS (Pre-request)✅ JS✅ JS
环境变量
导入 Postman✅ 一键导入❌ 需转换
桌面端✅ 原生应用❌ 浏览器/自部署✅ VSCode
开源协议MITMITApache 2.0MIT
Star 数30k+68k+35k+1.5k

2.3 关键决策点

经过实际试用,以下三点使我锁定了 Bruno:

1. 本地优先 + Git 原生支持

接口集合存储为纯文本 .bru 文件,结构清晰可读,天然支持 Git diff、code review、版本回滚。团队协作不再依赖第三方云,代码仓库即接口仓库。

2. 完全离线,无账号体系

没有强制登录,没有数据上传,内网环境、隔离环境照样用。这对安全敏感型团队是决定性优势。

3. 原生桌面应用,轻量快速

Bruno 用 Go + React 构建,启动秒开,内存占用远低于 Postman。虽然 2025 年开源版转为有限功能、商业版需要购买,但免费版的日常调试能力依然完整。
在这里插入图片描述


Bruno 这界面和postman是真像,布局操作按钮也差不太多
在这里插入图片描述

三、迁移实操:从导入到脚本改造

3.1 导入 Postman 集合

Bruno 官方提供了一键导入入口:

  1. 打开 Bruno,点击 Import Collection
  2. 选择 Postman Collection(支持 .json 导出文件)
  3. 选择导出的 Postman 集合文件,点击导入

导入后,每个 Postman 请求会转换成对应的 .bru 文件,目录结构基本保留。
在这里插入图片描述
注意:postman导出集合时变量不会完整的跟着导出,导入Bruno后自己比对原来集合的变量,缺的手动补齐。
在这里插入图片描述

3.2 环境变量迁移

Postman 的环境变量(Environment)需要在 Bruno 中重建:

  • Postman{{base_url}} 语法,配合 Environment 文件
  • Bruno:同样支持 {{base_url}} 语法,但环境定义在 Collection 级别

在 Bruno 中,点击集合右上角 Settings → Environment Variables,逐条添加:

{
  "base_url": "https://api.example.com",
  "username": "your_username",
  "password": "your_password",
  "token": ""
}

3.3 脚本迁移差异

这是迁移过程中工作量最大的部分。Postman 的 Pre-request Script / Tests 脚本需要改造成 Bruno 语法。

差异 1:变量读写
操作PostmanBruno
读取集合变量pm.collectionVariables.get("key")bru.getCollectionVar("key")
写入集合变量pm.collectionVariables.set("key", val)bru.setVar("key", val)
读取全局变量pm.globals.get("key")bru.getVar("key")
设置环境变量pm.environment.set("key", val)bru.setEnvVar("key", val)
差异 2:发送请求

Postman 用回调式,Bruno 用 async/await:

// Postman 回调式
pm.sendRequest(url, function (err, response) {
    console.log(response.json());
});

// Bruno async/await
const res = await bru.sendRequest({ url: url, method: 'GET' });
console.log(res.data);
差异 3:内置库依赖

Postman 提供 require() 能力(如 momentlodash),Bruno 不支持 require(),需用原生 JS 替代:

// Postman
var moment = require('moment');
var ts = moment().valueOf();

// Bruno(原生替代)
var ts = Date.now();

3.4 案例:登录获取 Token

Postman 原始脚本:

const baseurl = pm.collectionVariables.get('url_online');
const tokenurl = baseurl + "/api/v1/login/token";

pm.sendRequest({
    url: tokenurl,
    method: "POST",
    header: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    body: {
        mode: "urlencoded",
        urlencoded: [
            { key: "username", value: pm.collectionVariables.get("username") },
            { key: "password", value: pm.collectionVariables.get("password") }
        ]
    }
}, function (err, response) {
    const res = response.json();
    pm.collectionVariables.set("token", res.access_token);
});

Bruno 改造后:

const baseurl = bru.getCollectionVar('url_online');
const tokenurl = baseurl + "/api/v1/login/token";
console.log("tokenurl:" + tokenurl);

const res = await bru.sendRequest({
    url: tokenurl,
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    data: {
        "username": bru.getCollectionVar("username"),
        "password": bru.getCollectionVar("password")
    }
});

console.log(res.data);
console.log(res.data.access_token);
bru.setVar("token", res.data.access_token);

在这里插入图片描述

3.5 案例: SM2/SM3 签名

我的原脚本使用 Postman 的 require('moment') 和大量 pm.collectionVariables.set()

迁移要点:

// 1. 变量写入全部改为 bru.setVar
bru.setVar('sm3Hash', digest);
bru.setVar('sm2Sign', signature);
bru.setVar('sm2Enc', cipher);
bru.setVar('sm2Dec', plain);

// 2. moment 替换为原生 Date
// var moment = require('moment');
// var ts = moment().valueOf();
var ts = Date.now();
bru.setVar("ts", ts);

// 3. 生成签名方法不变
const sign = sm2.doSignature(signData, sm2prikey, {
    hash: true,
    publicKey: sm2pubkey,
    der: true
});

// 4. Hex → Base64
const hexSign = sign;
const byteArray = [];
for (let i = 0; i < hexSign.length; i += 2) {
    byteArray.push(parseInt(hexSign.substr(i, 2), 16));
}
const base64Sign = btoa(String.fromCharCode.apply(null, byteArray));
bru.setVar("cqvip-sign", base64Sign);

在这里插入图片描述

四、迁移常见报错与解决

4.1 ReferenceError: 'pm' is not defined

原因:脚本中残留 Postman 的 pm 全局对象。

解决:全文搜索 pm.,替换为 Bruno 对应 API(bru.getVar / bru.setVar / bru.getCollectionVar 等)。

4.2 ReferenceError: require is not defined

原因:Bruno 脚本环境不支持 Node.js 的 require()

解决:用原生 JS 实现替代:

原用途替代方案
require('moment')Date.now() / new Date()
require('lodash')原生数组/对象方法
require('crypto-js')Bruno 内置加密或手写实现

4.3 bru.sendRequest 返回 undefined

原因:仍在使用 Postman 的回调式写法。

解决:改用 await 直接接收返回值:

// 错误(回调式)
bru.sendRequest(url, function(err, res) { ... });

// 正确(async/await)
const res = await bru.sendRequest({ url: url, method: 'GET' });

4.4 动态加载外部 JS 库失败

Postman 中可以 eval 远程 JS,Bruno 中此方案受限。建议将依赖的算法库代码直接内联到脚本中,或改用 Bruno 支持的加载方式。
在这里插入图片描述

五、迁移后的收益

经过完整迁移,团队在以下方面获得明显改善:

  1. 接口即代码.bru 文件随业务代码一起纳入 Git 管理,接口变更可追溯、可 review
  2. 零云依赖:彻底摆脱账号和云端同步,内网/隔离环境畅通无阻
  3. 启动提速:从 Postman 的 10 秒到 Bruno 的秒开,日常调试体验显著提升
  4. 内存友好:内存占用下降一个数量级,显著缓解开发机寸土寸金的内存紧张问题

在这里插入图片描述

六、总结

阶段关键动作
调研明确"本地优先、Git 友好、离线可用"三大需求
选型对比 5+ 工具,锁定 Bruno
迁移一键导入集合 → 重建环境变量 → 改造脚本
踩坑解决 pmrequire、回调式三大差异
收益接口 Git 化、零云依赖、性能提升

Postman 到 Bruno 的迁移,本质上是**从"云优先"回到"本地优先"**的转变。对重视数据主权、追求轻量高效的团队而言,这是一条值得走的路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值