Pre-request脚本实战分享与常见问题解决方案
Pre-request脚本踩坑实录:从一脸懵到终于搞定
前几天做接口测试的时候,遇到一个特别棘手的问题。简单来说,我需要在Postman的Pre-request脚本里动态生成一个签名参数,但不管怎么写,总是报错或者结果不对。折腾了大半天才发现问题出在哪,这里记录一下踩过的坑和最终的解决方案。
问题出在哪?先说说我遇到的情况
事情是这样的,公司后端要求我们在请求某个API时,必须带一个基于时间戳和密钥生成的签名参数。这个签名的生成逻辑还挺复杂:先要把请求参数按字母排序,然后拼接成字符串,再加上一个密钥,最后用HMAC-SHA256算法加密。听起来不难对吧?但就是这个签名,让我差点崩溃。
一开始我的思路很直接:写个Pre-request脚本,在发送请求之前动态生成签名,然后把签名附加到请求参数里。可是实际运行时,发现签名总是不对,要么是格式错误,要么是加密结果跟后端的预期不一致。更气人的是,后端那边还说“我们的逻辑没问题”,潜台词就是“你前端搞错了”。
排查过程:试了N种方法才找到症结
既然后端甩锅给我,那我就只能硬着头皮自己找问题了。以下是我踩过的一些坑:
- 坑1:时间戳格式不对。 我一开始用的是Date.now(),以为直接拿毫秒数就行了,后来发现后端要的是秒级时间戳。这里我踩了个坑,改完之后还是不行。
- 坑2:字符串拼接顺序有问题。 后端要求按参数名的字母顺序排序后再拼接,但我一开始没注意到这一点,直接按照参数传入的顺序拼接,结果当然不对。
- 坑3:加密算法实现有偏差。 Postman自带的crypto库支持HMAC-SHA256,但它的返回值是Buffer对象,而我需要的是十六进制字符串。这里我又折腾了半天。
后来试了下发现,真正的问题其实是一个细节没处理好:参数拼接时忘了加上“=”号!比如key=value这种形式,我只拼了keyvalue,结果整个签名就全乱了。
核心代码就这几行:亲测有效的解决方案
废话不多说,直接上最终的代码。这段代码是我反复调试了好几遍才写出来的,目前已经稳定运行了好几天,基本上没啥问题了。
// Pre-request Script: 动态生成签名
const crypto = require('crypto');
// 获取当前时间戳(秒级)
const timestamp = Math.floor(Date.now() / 1000);
// 请求参数(模拟)
const params = {
key1: "value1",
key2: "value2",
timestamp: timestamp
};
// 按照字母顺序排序参数
const sortedParams = Object.keys(params)
.sort()
.reduce((acc, key) => {
acc[key] = params[key];
return acc;
}, {});
// 拼接成字符串(注意带上"="号!)
let paramString = "";
for (const [key, value] of Object.entries(sortedParams)) {
paramString += ${key}=${value}&;
}
paramString = paramString.slice(0, -1); // 去掉最后一个"&"
// 加密生成签名
const secret = "your-secret-key"; // 替换为实际的密钥
const hmac = crypto.createHmac('sha256', secret);
hmac.update(paramString);
const signature = hmac.digest('hex');
// 将签名添加到环境变量中
pm.environment.set("signature", signature);
聊聊技术细节:为什么这样写
上面这段代码虽然看起来简单,但有几个关键点值得展开讲讲:
- 时间戳处理: 注意这里用了
Math.floor(Date.now() / 1000)来生成秒级时间戳,而不是直接用Date.now()。如果你的时间戳单位不对,后端可能直接拒绝你的请求。 - 参数排序: JavaScript的
Object.keys().sort()可以很方便地对参数名进行字母排序,但别忘了用reduce()重新组装成对象。 - 字符串拼接: 这里的关键是一定要带上“=”号,否则后端解析参数时会出问题。另外,记得去掉最后一个多余的“&”符号。
- 加密算法: Postman内置的crypto库非常强大,但它的
digest()方法默认返回的是Buffer对象,所以一定要指定输出格式为hex。
另外还有一个小细节:我把生成的签名存到了Postman的环境变量里(pm.environment.set),这样在后续的请求中可以直接使用{{signature}}来引用它。这种方式不仅方便,还能避免重复计算。
还有个小问题:改完后仍有一点瑕疵
虽然这段代码已经能正常工作了,但还是有个小问题:如果请求参数里有数组或嵌套对象,这段代码就处理不了了。举个例子,像key=[value1,value2]这种格式,后端可能会要求你手动展开成key=value1&key=value2的形式,但目前的代码还没法做到这一点。
不过好在这个接口暂时用不到复杂参数,所以我也没继续深究。如果以后真遇到了这种情况,可能得考虑用第三方库(比如Lodash)来处理参数展平的问题。
以上是我踩坑后的总结
总的来说,这次的Pre-request脚本问题虽然不算特别复杂,但确实暴露了我在一些细节上的疏忽。特别是参数拼接和加密算法的实现上,稍微不注意就会导致整个签名失效。
如果你也在用Postman做接口测试,建议多留意这些容易踩坑的地方。另外,如果你有更好的解决方案,或者知道如何优雅地处理复杂参数,欢迎在评论区交流!

暂无评论