Pre-request脚本实战分享与常见问题解决方案

UX-传志 工具 阅读 2,075
赞 9 收藏
二维码
手机扫码查看
反馈

Pre-request脚本踩坑实录:从一脸懵到终于搞定

前几天做接口测试的时候,遇到一个特别棘手的问题。简单来说,我需要在Postman的Pre-request脚本里动态生成一个签名参数,但不管怎么写,总是报错或者结果不对。折腾了大半天才发现问题出在哪,这里记录一下踩过的坑和最终的解决方案。

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做接口测试,建议多留意这些容易踩坑的地方。另外,如果你有更好的解决方案,或者知道如何优雅地处理复杂参数,欢迎在评论区交流!

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论