目录
apipost脚本使用教程
缘由:
实现流程:
1、设置接口需要的URL:
2、boby:
3、预执行操作:
4、断言
5、执行结果:
什么是ApiPost?
下载以及安装:
apipost使用文档介绍:
发送前动态修改Query、Body、Header参数
1、使用场景
2、脚本语法
3、实际项目操作例如
利用预执行脚本动态添加一个请求参数
使用场景
body请求参数如下:
header请求参数如下:
具体实现
1、计算token并赋给变量
2、动态添加请求头
3、实际项目操作例如
请求一个需要登录才能访问的接口(基于COOKIE)
方案II、利用环境变量,先请求登陆接口,再请求后续接口
利用CryptoJS对请求参数进行MD5、AES加解密
MD5加密
SHA256加密
base64加密
base64解密
AES简单加密
AES简单解密
自定义AES加解密函数
请求示例
请求接口自动计算参数签名
使用场景
脚本语法
实际项目操作例如
如何使用断言
什么是断言
使用 Apipost 的断言格式
响应结果的校验
数据校验的意义
数据校验的设置
常见问题
apipost脚本使用教程
缘由:
本期文章主要围绕脚本实现模拟第三方推送接口讲解
主要原因:项目涉及到需要对接第三方接口,暴露接口出去给第三方调用。每次项目订单状况节点,需要等待第三方开单后,在推送下单状态回来,每次多人操作时候,都需要登录同一账号,过程等待比较缓慢,效率比较低。
实现流程:
1、设置接口需要的URL:
http://localhost:8080/api/dbts/sendOrderStatus
2、boby:
参数名 | 参数值 | 描述 |
---|---|---|
digest | String | {{digest}} |
params | String | {{params}} |
timestamp | String | 1731572100579 |
companyCode | String | {{companyCode}} |
logisticID | String | TSVU43434t318000045 |
mailNo | String | sdferee340985266 |
statusType | String | GOT |
3、预执行操作:
class SecurityUtil {static getDigest(plainText) {// 使用 crypto 模块生成 MD5 哈希let md5Str = CryptoJS.MD5(plainText);// 将 MD5 哈希转换为 Base64 编码的字符串let base64Encoded = btoa(md5Str);return base64Encoded;}
}let timestamp = request.request_bodys.timestamp.toString();
apt.globals.set("timestamp", timestamp);let companyCode = "*********"; //账号编码
apt.globals.set("companyCode", companyCode);let appkey = "********************"; //账密let params = "";
let logisticID = request.request_bodys.logisticID.toString();
let mailNo = request.request_bodys.mailNo.toString();
let statusType = request.request_bodys.statusType.toString();
console.log("logisticID: " + logisticID + " ,mailNo: " + mailNo + " ,statusType: " + statusType );
apt.removeRequestBody("logisticID");
apt.removeRequestBody("mailNo");
apt.removeRequestBody("statusType");
const model = packingCommonModel();
params = JSON.stringify(model);
apt.globals.set("params", params);let digest = "";
digest = SecurityUtil.getDigest(params + appkey + timestamp);
console.log("digest: ", JSON.stringify(digest));
apt.globals.set("digest", digest);function packingCommonModel() {const commonModel = {gmtUpdated: "1724375950500",logisticCompanyID: "DEPPON",logisticID: logisticID,mailNo: mailNo,statusType: statusType};return commonModel;
}
4、断言
后执行操作:
apt.test("响应json的errstr字段值为'success'", function () {var jsonData = apt.response.json();apt.expect(jsonData.errstr).to.eql("success");
});apt.test("成功的POST请求", function () {apt.expect(apt.response.code).to.be.oneOf([201, 202]);
});
5、执行结果:
断言与校验结果:
什么是ApiPost?
Apipost 基于同一份数据源,同时提供供后端开发、前端开发、测试人员使用的接口调试、Mock、自动化测试等功能,实时协作,降本增效绝不是空谈!
同时,针对技术管理层来说,也大大方便了 API 文档类数字资产的管理与延续。
视频教程:ApiPost视频培训教程
下载以及安装:
支持Windows、Mac、Linux等多种操作系统
下载地址:
apipost下载地址
下载后,直接解压安装就OK了。如果对安装有问题的朋友,各位自行百度一下!!!
apipost使用文档介绍:
https://v7-wiki.apipost.cn/docs/4
发送前动态修改Query、Body、Header参数
在文档 “预执行脚本” 中,我们了解到,预执行脚本的作用主要是:
编写JS函数等实现复杂计算;
变量的打印
定义、获取、删除、清空环境变量
定义、获取、删除、清空全局变量
获取请求参数
动态添加、删除一个header请求参数
动态添加、删除一个query请求参数
动态添加、删除一个body请求参数
发送HTTP请求
利用这个功能,我们可以在发送前动态的添加或者修改请求参数。
1、使用场景
适用于在请求发送前,需要动态改变请求参数的情况。例如:请求发送时,需要通过其他请求参数计算一个token同时添加到对应的发送参数中。
2、脚本语法
apt.setRequestQuery("key", "value"); // 给URL添加、修改query参数
apt.removeRequestQuery("key"); // 删除URL的指定query参数
apt.setRequestHeader("key", "value"); // 增加、修改一个请求头key
apt.removeRequestHeader("key"); // 删除请求头 key
apt.setRequestBody("key", "value"); // 增加、修改一个body参数
apt.removeRequestBody("key"); // 删除body参数key
apt.setRequestBody({ "key": "value" }
); // 将 body 参数 "整体" 重置为 {"key": "value"},适用于原请求体为 raw 类型的情况
3、实际项目操作例如
totalVolume 和 totalWeight 获取值之后,需要在请求体删除掉
脚本
let totalVolume = request.request_bodys.totalVolume.toString();
let totalWeight = request.request_bodys.totalWeight.toString();
apt.removeRequestBody("totalVolume");
apt.removeRequestBody("totalWeight");
利用预执行脚本动态添加一个请求参数
使用场景
我们可能需要在请求时发送一些参数,而这些参数是通过其他参与发送的请求参数计算而来的。举例:
body请求参数如下:
参数 | 参数描述 |
---|---|
user_id | 用户id |
nick_name | 用户名 |
header请求参数如下:
参数 | 参数描述 |
---|---|
token | 由body的请求参数user_id、nick_name通过md5 加密后组成 |
如上面的情况,我们需要在发送前,先通过body的请求参数user_id、nick_name通过md5 加密计算得出一个token放到header里才行。ApiPost如何实现这个需求呢?
我们可以通过在预执行脚本里增加请求参数来实现。
具体实现
如下图,我们已经在body里添加好了我们需要的参数。
1、计算token并赋给变量
我们接下来要做的是通过预执行脚本计算出token并添加到header参数。
先在预执行脚本里定义一个临时变量raw_token,其值由
let raw_token = $.md5(request.request_bodys.user_id.toString() + request.request_bodys.nick_name.toString());
其中的含义为:定义一个变量raw_token,其值等于
$.md5(request.request_bodys.user_id + request.request_bodys.nick_name)
注:$.md5 是APIPOST脚本内置的md5函数。更多加密函数可以参考 “利用CryptoJS对请求参数进行MD5/AES加解密” 一节。
2、动态添加请求头
apt.setRequestHeader("token", raw_token);
发送后,可以看到APIPOST自动添加了一个请求头token
3、实际项目操作例如
mailNo | String | sdferee340985266 |
statusType | String | GOT |
脚本:
let mailNo = request.request_bodys.mailNo.toString();
let statusType = request.request_bodys.statusType.toString();
console.log(mailNo: " + mailNo + " ,statusType: " + statusType );
请求一个需要登录才能访问的接口(基于COOKIE)
在后台在开发、调试接口时,常常会遇到需要登陆才能请求的接口。
比如:获取登陆用户的收藏列表,此时,我们就需要模拟登陆状态进行接口调试了。如图:
.png)
今天,我们讲解利用ApiPost的环境变量,解决这种需要先登录再请求的接口依赖情况。
ApiPost提供了2种方案:
方案I、开启全局cookie
apipost提供了开启全局cookie的功能。开启路径如下:
右下角Cookie管理器-打开全局Cookie按钮
开启后,我们请求登陆接口后,后续接口都会共享“已登陆”的状态,即共享了登陆接口返回的cookie。
如下所示:
第一步:请求登陆接口
第二步:访问其他接口,则都处于了登陆状态
方案II、利用环境变量,先请求登陆接口,再请求后续接口
这种方案是针对关闭了全局cookie功能的情况。
为了处于登陆态,需要先请求登陆接口,此举目的是为了模拟用户的登陆行为,获取需要的登陆参数(这里是Cookie)。
将登陆接口返回的PHPSESSID(这个是SessionID,PHPSESSID是针对PHP作为后端接口的SessionID变量名,其他语言的变量名可能不同)设为环境变量。
apt.variables.set("login_var", response.cookies["PHPSESSID"]);
注:更多响应结果绑定变量可以参考 “响应以及断言”一节和“后执行脚本” 一节。
接着返回收藏接口,进到header选项,参数值选择cookie,参数值输入: PHPSESSID={{login_var}}。
此举是为了利用登陆接口返回的Cookie伪造请求的PHPSESSID。
如图:
或者你也可以定义个全局header,这样就不用每个接口都设置一遍了:
登录实现原理
利用ApiPost发送Cookie,使服务器识别已登录用户的Cookie。
利用CryptoJS对请求参数进行MD5、AES加解密
ApiPost内置了CryptoJS( GitHub - brix/crypto-js: JavaScript library of crypto standards. ) ,可以方便的对请求参数进行各种加解密。
MD5加密
CryptoJS.MD5('待加密字符串').toString()
SHA256加密
base64加密
CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('待加密字符串'))
base64解密
CryptoJS.enc.Base64.parse("待解密字符串").toString(CryptoJS.enc.Utf8)
AES简单加密
CryptoJS.AES.encrypt('待加密字符串', '秘钥').toString()
AES简单解密
CryptoJS.AES.decrypt('待解密字符串', '秘钥').toString(CryptoJS.enc.Utf8)
自定义AES加解密函数
以上示例是2个简单aes加解密方案,大部分情况下,我们需要自定义aes加解密更多的参数,比如加密模式、填充等。
const key = CryptoJS.enc.Utf8.parse("秘钥"); //十六位十六进制数作为密钥
const iv = CryptoJS.enc.Utf8.parse('偏移量'); //十六位十六进制数作为密钥偏移量//解密方法
function Decrypt(word) {let encryptedHexStr = CryptoJS.enc.Hex.parse(word);let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);let decrypt = CryptoJS.AES.decrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);return decryptedStr.toString();
}//加密方法
function Encrypt(word) {let srcs = CryptoJS.enc.Utf8.parse(word);let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });return encrypted.ciphertext.toString().toUpperCase();
}//以上方法中 mode就是加密模式,padding是填充。
请求示例
请求接口自动计算参数签名
使用场景
某些接口需要通过参数签名验证接口请求合法性。 例如签名规则如下:将请求参数param1, param2,按照appid+param1+parma2+salt+secretKey 的顺序拼接得到字符串1。 第二步:对字符串1做md5,得到32位小写的sign。
脚本语法
使用预执行脚本实现这个需求,实现demo如下
// 获取 query 参数 param1, param2
var queryParams = request.request_querys;
console.log(queryParams);
var param1 = queryParams['param1'];
var param2 = queryParams['param2'];// 获取预先设置为环境变量的 APPID 和 SECRET_KEY
var appid = apt.variables.get("APPID");
var secretKey = apt.variables.get("SECRET_KEY");// 定义一个随机数(11111 65536)之间
var salt = parseInt(Math.random() * (54425) + 11111, 10);// 将随机数转换为字符串
salt = salt.toString();
console.log(salt);// 定义一个由appid、要翻译的字符串、随机数、密钥组合成一个字符串
var str = appid + param1 + param2 + salt + secretKey;
console.log(str);// 将 str 进行 md5 加密生成 sign
var sign = CryptoJS.MD5(str).toString();apt.setRequestQuery("salt", salt); //设置salt
apt.setRequestQuery("sign", sign); //设置签名
实际项目操作例如
boby:
脚本:
class SecurityUtil {static getDigest(plainText) {// 使用 crypto 模块生成 MD5 哈希let md5Str = CryptoJS.MD5(plainText);// 将 MD5 哈希转换为 Base64 编码的字符串let base64Encoded = btoa(md5Str);return base64Encoded;}
}let timestamp = request.request_bodys.timestamp.toString();
apt.globals.set("timestamp", timestamp);let companyCode = "*********"; //账号编码
apt.globals.set("companyCode", companyCode);let appkey = "********************"; //账密let params = "";
let logisticID = request.request_bodys.logisticID.toString();
let mailNo = request.request_bodys.mailNo.toString();
let statusType = request.request_bodys.statusType.toString();
console.log("logisticID: " + logisticID + " ,mailNo: " + mailNo + " ,statusType: " + statusType );
apt.removeRequestBody("logisticID");
apt.removeRequestBody("mailNo");
apt.removeRequestBody("statusType");
const model = packingCommonModel();
params = JSON.stringify(model);
apt.globals.set("params", params);let digest = "";
digest = SecurityUtil.getDigest(params + appkey + timestamp);
console.log("digest: ", JSON.stringify(digest));
apt.globals.set("digest", digest);function packingCommonModel() {const commonModel = {gmtUpdated: "1724375950500",logisticCompanyID: "DEPPON",logisticID: logisticID,mailNo: mailNo,statusType: statusType};return commonModel;
}
打印结果:
如何使用断言
什么是断言
断言一般用于 后执行脚本
用来校验响应结果是否符合预期。
在协作开发、版本升级、服务器升级、接口返回的过程中,有可能因为一些bug,和我们预期的结果不一致。为了便于开发&测试人员能够更快的发现bug,保证整个产品的质量以及进度,于是我们推出了断言功能。
- 定义测试用例
- 验证测试用例
例如接口返回:
{"errcode": 0,"errstr": "success","post": {"body": "test"},"get": [],"request": {"body": "test"},"file": [],"put": "","header": {"User-Agent": "ApiPOST Runtime +https://www.apipost.cn","Accept": "*/*","Accept-Encoding": "gzip, deflate, br","Connection": "keep-alive","Hello": "Tom","Cookie": "cookie-test5=nihao;cookie-test1=0;cookie-test2=0;cookie-test8=renge;cookie-test3=%25E4%25BD%25A0%25E5%25A5%25BD;cookie-test4=%E4%BD%A0%E5%A5%BD;httponly-cookie=httponly-value","Host": "echo.apipost.cn","Content-Type": "multipart/form-data; boundary=--------------------------856985481572999749293071","Content-Length": "163"},"cookie": {"cookie-test5": "nihao","cookie-test1": "0","cookie-test2": "0","cookie-test8": "renge","cookie-test3": "%E4%BD%A0%E5%A5%BD","cookie-test4": "你好","httponly-cookie": "httponly-value"},"bigint": 248963637882912768
}
定义测试用例:
apt.assert('response.raw.status==200');
apt.assert('response.raw.type=="json"');
apt.assert('response.json.errcode==0');
apt.assert('response.raw.responseTime<100');
apt.assert('response.json.header.Host=="echo.Apipost.cn"');
点击发送按钮后:
绿色表示测试通过,红色表示测试不通过。
特别注意:==每个测试用例是一行,不能换行。==
例:apt.assert('response.json.header.Host=="echo.Apipost.cn"');
1)response.json.header.Host 表示响应json下面的header数组中的Host字段,
2)必须都为1,才会通过。
常见的测试断言可以通过后执行脚本获取: (常用断言示例)
使用 Apipost 的断言格式
// 检查response body中是否包含某个string
apt.assert('response.raw.responseText=="test"'); // 检查响应文本是否等于test字符串 apt.assert('response.raw.responseText.indexOf("test") > -1'); // 检查响应文本是否含有test字符串// 检测返回JSON中的某个值是否等于预期的值
apt.assert('response.json.hasOwnProperty("errcode")'); // 检测返回json对象的是否含有errcode字段
apt.assert('response.json.errcode=="success"'); // 检测返回json对象的errcode字段是否等于success字符串
apt.assert('response.json.errcode.indexOf("success") > -1'); // 检测返回json对象的errcode字段是否含有success字符串
apt.assert('response.json.errcode!="success"'); // 检测返回json对象的errcode字段是否不等于success字符串
apt.assert('response.json.errcode>=1'); // 检测返回json对象的errcode字段是否大于1
apt.assert('response.json.errcode==null'); // 检测返回json对象的errcode字段是否是null// 测试response Headers中的某个元素是否存在(如:Content-Type)
apt.assert('response.headers.hasOwnProperty("content-type")');// 验证Status code(响应码)的值是不是等于200
apt.assert('response.raw.status==200');// 验证Response time(请求耗时)是否大于某个值
apt.assert('response.raw.responseTime>=100');
响应结果的校验
数据校验的意义
我们可以通过 json-schema
预先定义接口的数据返回格式,当接口完成后,我们可以通过匹配 实际响应结果
和 预先定义的接口格式
,来发现接口问题。如下图:
数据校验的设置
我们可以通过在 设计
- 预定义响应期望
- json-schema
预先定义接口的数据返回格式。如下图:
常见问题
注意
有用户问,为什么我返回的内容明明是 json
格式,而数据校验提示 : 返回数据格式不是json
这是因为虽然您返回的内容是 JSON
字符串,但是返回的格式却是其他类型(可以具体查看响应头的 content-type
)。相当于明明是一张图片内容,您却保存为了 .txt
格式一样的道理。
本文章编辑到处结束,希望对各位有帮助!!!