功能简介
前置操作和后置操作都是 API 请求在发送和响应过程中执行的脚本,主要用于在发起 API 请求前和获得响应后完成验证或执行某些操作,目的是为了提高 API 调试和测试的效率,并确保接口的正确性。
前置操作
前置操作是在 API 请求之前执行的脚本代码,可以用于做以下事情:
- 设置 API header(请求头)它们可用于设置请求的请求头、请求正文、验证请求参数和配置身份验证等。
- 设置 API 参数前置操作能够访问环境变量、全局变量和请求变量中的数据。前置操作也可以帮助请求者了解请求参数以及如何处理它们。
- 添加身份验证例如基本身份验证或 OAuth。在发送 API 请求之前,前置操作可以被用来获取访问令牌或者其他权限,确保 API 请求发送的是有效的和合法的请求。
变量替换
“变量替换”功能通常用于在发送 API 请求前,作用是把接口请求参数里的所有的已引用变量(包括动态值)替换成真实的请求内容。通常用于处理接口签名等转换场景。此时主要涉及以下两种场景:
- 通过脚本 set 变量该场景下的操作需要放在“变量替换”之前运行,否则通过该脚本 set 的变量就不会对当前接口的请求参数生效。
- 接口签名脚本该场景下的脚本需放在“变量替换”之后,这样脚本才能获取到该接口实际请求的参数值,否则获取到的参数值是变量替换之前的模板数据。
如需了解更多,请参考《接口签名如何处理?》
后置操作
后置操作能够利用获取来自接口响应的数据,例如状态代码、header、body 等信息,进行二次处理:
- 验证 API 响应的状态码和响应时间是否符合预期。
- 验证 API 响应的内容,如 JSON 或 XML 数据。
- 从 API 响应中提取数据,并将其用于后续请求。
- 自动提取响应中需要的数据。
层级关系
前后置操作都可以在接口目录中设置父级操作。父级操作可以被继承到该目录下的所有接口中,适用于需要在多个接口中执行相同的前置操作的场景,例如鉴权,变量替换等。 接口本身可以灵活调整各操作的运行顺序。
子级接口可以选择是否采用父级操作。
当子级接口下又存在多个接口时,若选择关闭引用,次级接口将默认关闭该自定义脚本。
提取变量
在一些具有明显上下游关系的接口中,有时需要将 A 接口的返回数据作为 B 接口的请求参数。
比如在创建宠物信息场景下,需要将 A 接口返回的
pet_id
中的数据作为后续接口的请求参数,然后在 B 接口中的「后置操作」中添加「提取变量」功能,基于 A 接口返回的结果自动提取数据并设置为变量(临时变量/环境变量/全局变量),方便其它接口运行的时候直接使用。
指定变量类型
打开 Apifox 中的某条接口,在“后置操作”页中添加“提取变量”,变量类型选择为“环境变量”。
接口间相互传递数据
例如当前 B 接口的请求参数依赖于 A 接口返回的数据,现希望 B 接口在发送请求的同时能自动获取 A 接口返回的数据并作为请求参数。实现思路如下:
- A 接口在后置操作中添加提取变量功能,将返回的数据自动提取至变量中。
- B 接口对应的参数值直接引用已提取的变量。
A 接口
打开 A 接口用例的”后置操作“页,在后置操作中添加提取变量功能。将接口返回 Response JSON 数据里的 token 值提取到名为
petId
的变量中。
B 接口
在 B 接口中的请求参数中直接填写
{{petId}}
,即可在请求中引用上一步骤中所创建的数值。
若不确定是否正确引用了 A 接口所生成的数据,可以在 B 接口的前置操作中添加以下自定义脚本:
var petId = pm.environment.get("petId");
console.log(petId)
这样就可以在控制台中查看已引用的变量值。
JSON Path 介绍
JSONPath 之于 JSON,就如 XPath 之于 XML。JSONPath 可以方便对 JSON 数据结构进行内容提取。
如果对 JSON Path 不熟悉,推荐使用这个免费的 AI 工具来生成:https://app.anakin.ai/apps/21854
概览
- 根对象使用
$
来表示,而无需区分是对象还是数组 - 表达式可以使用
.
,也可以使用[]
。如:$.store.book[0].title
或$['store']['book'][0]['title']
- 表达式
(<expr>)
可用作显式名称或索引的替代,如:$.store.book[(@.length-1)].title
:表示获取最后一个 book 的 title - 使用符号
@
表示当前对象。过滤器表达式通过语法支持,?(<boolean expr>)
如:$.store.book[?(@.price < 10)].title
:表示获取价格小于 10 的所有 book 的 title
语法
要点:
$
表示文档的根元素@
表示文档的当前元素.node_name
或['node_name']
匹配下级节点[index]
检索数组中的元素[start:end:step]
支持数组切片语法*
作为通配符,匹配所有成员..
子递归通配符,匹配成员的所有子元素(<expr>)
使用表达式?(<boolean expr>)
进行数据筛选
JSONPath 语法和 XPath 对比:
XPathJsonPath说明
/
$
文档根元素
.
@
当前元素
/
.
或
[]
匹配下级元素
..
N/A
匹配上级元素,JsonPath 不支持此操作符
//
..
递归匹配所有子元素
*
*
通配符,匹配下级元素
@
N/A
匹配属性,JsonPath 不支持此操作符
[]
[]
下标运算符,根据索引获取元素,XPath 索引从 1 开始,JsonPath 索引从 0 开始``
[,]
N/A
[start:end:step]
数据切片操作,XPath 不支持
[]
?()
过滤表达式
N/A
()
脚本表达式,使用底层脚本引擎,XPath 不支持
()
N/A
分组,JsonPath 不支持
注意:
- JsonPath 的索引从0开始计数
- JsonPath 中字符串使用单引号表示,例如:
$.store.book[?(@.category=='reference')]
中的'reference'
JsonPath 示例
下面是相应的 JsonPath 的示例:
{"store":{"book":[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],"bicycle":{"color":"red","price":19.95}}}
对这个文档进行解析:
XPathJsonPathResult
/store/book/author
$.store.book[*].author
所有 book 的 author 节点
//author
$..author
所有 author 节点
/store/*
$.store.*
store 下的所有节点,book 数组和 bicycle 节点
/store//price
$.store..price
store 下的所有 price 节点
//book[3]
$..book[2]
匹配第 3 个 book 节点
//book[last()]
$..book[(@.length-1)]
,或
$..book[-1:]
匹配倒数第 1 个 book 节点
//book[position()<3]
$..book[0,1]
,或
$..book[:2]
匹配前两个 book 节点
//book[isbn]
$..book[?(@.isbn)]
过滤含 isbn 字段的节点
//book[price<10]
$..book[?(@.price<10)]
过滤
price<10
的节点
//*
$..*
递归匹配所有子节点
使用脚本
脚本介绍
Apifox 包含一个基于
Javascript
的脚本引擎,通过脚本(
JavaScript
代码片段)可实现在接口请求或集合测试时添加动态行为。脚本示例详见
脚本功能:
- 使用后置脚本功能测试(断言)请求返回结果的正确性。
- 使用前置脚本动态修改接口请求参数,如增加接口签名参数等。
- 使用脚本操作变量并在接口请求之间传递数据。
- 支持全局设置(在根目录里设置)前置操作、后置操作,设置后项目里的所有接口运行时都会生效。
- 支持分组里设置前置操作、后置操作,设置后分组里的所有接口运行时都会生效。
- 接口请求的执行流程:[全局前置脚本] -> [分组前置脚本] -> [接口前置脚本] -> [发送接口请求] -> [返回接口结果] -> [全局后置脚本] -> [分组后置脚本] -> [接口后置脚本]。
- 脚本调试:调试脚本可以在 前置脚本 和 后置脚本 里编写,使用
console.log('hello')
方式将调试信息写入控制台,打开 控制台 即可查看。 - 脚本可以直接 调用其他语言编写的程序,支持
java(.jar)
、python
、php
、js
、BeanShell
、go
、shell
、ruby
、Lua
等语言编写的外部程序。
注:Apifox 完美兼容 Postman 脚本语法,可以将 Postman 脚本可以无缝迁移至 Apifox。
前置脚本
- 前置脚本是在请求发送前执行的代码片段。例如要在请求头中包含时间戳或在 URL 参数中发送随机的字母、数字、字符串等数据。
- 前置脚本使用 JavaScript 编写,语法与后置脚本完全相同,但不存在
pm.response
对象。 - 可以在接口的 “前置操作” tab 页中添加前置脚本,发送接口请求前将自动运行前置脚本。
示例-设置环境变量
- 若在请求参数中包含当前时间戳,那么可以将函数返回值设置为环境变量。
- 将参数 timestamp 的值设置为
{{timestamp}}
。当请求发送时,前置脚本将被执行,环境变量 timestamp 的值会被设置为当前时间戳,同时{{timestamp}}
也会被替换为当前时间戳。
示例-获取 token 并添加到请求头
若在请求头需要携带 token,那么可以将获取 token 接口返回的 token 值直接添加到请求头
// token环境变量值为null,调用token获取接口并将值存入token环境变量var requestUrl ='https://test.com/api/usertoken?appKey=aaa&appSecret=bbb'
pm.sendRequest(requestUrl,function(err, response){if(err){
console.error(err);return;}var respData =JSON.parse(response.text());var token = respData.data;
console.log(token)// 将token放到环境变量,请求头token键值引用环境变量 //pm.environment.set('X-GV-3RD-USER-TOKEN', token);// 直接把token键值加入到请求头中
pm.request.headers.add({key:"X-GV-3RD-USER-TOKEN",value: token
});});
更多示例
- 环境变量、全局变量、临时变量使用示例
- 读取/修改接口请求信息
- 脚本内发送接口请求示例:
pm.sendRequest('https://www.api.com/get',function(err, response){ console.log(response.json());});
- 加密/解密
后置脚本
- 发送接口请求后执行的代码片段也称为后置脚本。主要用来断言请求返回的结果是否正确、将请求返回的结果数据写入环境变量等场景。
- 可以在接口的 “后置操作” tab 页中添加后置脚本,接口返回响应后将自动运行后置脚本。
示例-断言请求响应是否正确
// pm.response.to.have 示例
pm.test('返回结果状态码为 200',function(){
pm.response.to.have.status(200);});// pm.expect() 示例
pm.test('当前为正式环境',function(){
pm.expect(pm.environment.get('env')).to.equal('production');});// response assertions 示例
pm.test('返回结果没有错误',function(){
pm.response.to.not.be.error;
pm.response.to.have.jsonBody('');
pm.response.to.not.have.jsonBody('error');});// pm.response.to.be* 示例
pm.test('返回结果没有错',function(){// assert that the status code is 200
pm.response.to.be.ok;// info, success, redirection, clientError, serverError, are other variants// assert that the response has a valid JSON body
pm.response.to.be.withBody;
pm.response.to.be.json;// this assertion also checks if a body exists, so the above check is not needed});
示例-将接口响应数据写入至环境变量
// 获取 JSON 格式的请求返回数据var jsonData = pm.response.json();// 将 jsonData.token 的值写入环境变量
pm.environment.set('token', jsonData.token);
更多示例
- 更多测试/断言示例
- 使用变量(环境变量、全局变量、临时变量)示例
- 读取接口请求信息
- 加密/解密
公共脚本
- 公共脚本主要用途是实现脚本复用,避免在多个地方重复编写相同功能的脚本。
- 可以将频繁被引用的脚本或通用的类与方法编写至公共脚本中,然后在接口中直接引用。
- 管理公共脚本:在 “项目设置” - “公共脚本” 菜单中进行管理
- 引用公共脚本:点击接口中的“前置操作”或“后置操作”页,在此处引用公共脚本。发起接口请求时将优先运行公共脚本;公共脚本的运行顺序与添加顺序保持一致。
- 调用公共脚本: - 脚本之间是可以实现相互调用,以下是具体的使用场景: - 普通脚本使用纯函数通过 return 返回的方式调用公共脚本,不建议使用
pm.sendRequest
和pm.environments.set
方法。- 公共脚本之间支持相互调用。- 为了避免脚本之间的变量冲突,所有脚本执行的时候都是在各自的作用域(通过闭包包裹)下运行。- 若使用 var、let、const、function 声明的变量或者方法,那么归属于局部变量或局部方法,无法被其他脚本调用的。如果想要使得变量或方法被其他脚本调用,需要将脚本改成全局变量或全局方法。- 注意:- 请确保不同脚本之间全局变量或者全局方法命名间没有冲突。- 调用脚本需要注意脚本执行顺序,只有后置的脚本可以调用先执行的脚本。
变量示例
- 示例 A
// 声明局部变量,无法被其他脚本调用var my_var ="hello";
将代码修改为以下格式:// 声明全局变量,可以被其他脚本调用my_var ="hello";
- 示例 B
// 声明局部方法,无法被其他脚本调用functionmy_fun(name){ console.log("hello"+ name);}
将代码修改为以下格式:// 声明全局方法,可以被其他脚本调用my_fun=function(name){ console.log("hello"+ name);};
pm 对象 API(全局方法)
pm
pm:Object
pm 对象包含了接口(或测试集)运行的相关信息,并且可以通过它访问需要发送的请求信息和发送后返回的结果信息。另外还可以通过它get
或set
环境变量和全局变量。pm.info:Object
pm.info 对象包含了接口(或测试集)运行的相关信息。- pm.info.eventName:String当前执行是什么类型的脚本:前置脚本(prerequest),或后置脚本(test)。- pm.info.iteration:Number当前执行第几轮循环(iteration),仅集合测试有效。- pm.info.iterationCount:Number本次执行需要循环的总轮数,仅集合测试有效。- pm.info.requestName:String当前正在运行的接口用例名称- pm.info.requestId:String当前正在运行的接口用例名称的唯一 ID
pm.sendRequest
pm.sendRequest:Function 用途为在脚本内异步发送 HTTP/HTTPS 请求。
- 该方法接受一个 collection SDK 兼容的 request 参数和一个 callback 函数参数。callback 有 2 个参数,第一个是 error ,第二个是 collection SDK 兼容的 response。更多信息请查阅 Collection SDK 文档
- 在前置脚本和后置脚本都可以使用。
示例:
// GET 请求示例
pm.sendRequest("https://postman-echo.com/get",function(err, res){if(err){
console.log(err);}else{
pm.environment.set("variable_key","new_value");}});// 完整的 request 参数示例const echoPostRequest ={url:"https://postman-echo.com/post",method:"POST",header:{headername1:"value1",headername2:"value2",},// body 为 x-www-form-urlencoded 格式body:{mode:"urlencoded",// 此处为 urlencoded// 此处为 urlencodedurlencoded:[{key:"account",value:"apifox"},{key:"password",value:"123456"},],},/*
// body 为 form-data 格式
body: {
mode: 'formdata', // 此处为 formdata
// 此处为 formdata
formdata: [
{ key: 'account', value: 'apifox' },
{ key: 'password', value: '123456' }
]
}
// body 为 json 格式
header: {
"Content-Type": "application/json", // 注意:header 需要加上 Content-Type
},
body: {
mode: 'raw',// 此处为 raw
raw: JSON.stringify({ account: 'apifox', password:'123456' }), // 序列化后的 json 字符串
}
// body 为 raw 或 json 格式
body: {
mode: 'raw',
raw: '此处为 body 内容',
}
*/};
pm.sendRequest(echoPostRequest,function(err, res){
console.log(err ? err : res.json());});// 对返回结果进行断言
pm.sendRequest("https://postman-echo.com/get",function(err, res){if(err){
console.log(err);}
pm.test("response should be okay to process",function(){
pm.expect(err).to.equal(null);
pm.expect(res).to.have.property("code",200);
pm.expect(res).to.have.property("status","OK");});});
参考:
- Request JSON 结构
- Response 结构
pm.variables
临时变量:
pm.variables.has(variableName:String):function → Boolean
: 检查是否存在某个临时变量。pm.variables.get(variableName:String):function → *
: get 单个临时变量。pm.variables.set(variableName:String, variableValue:String):function → void
: set 单个临时变量。pm.variables.replaceIn(variableName:String):function
: 以真实的值替换字符串里的包含的动态变量,如{{variable_name}}
。pm.variables.toObject():function → Object
: 以对象形式获取所有临时变量。
不同类型的变量,有不同的优先级,不同类型变量的优先级顺序为: 临时变量 > 环境变量 > 全局变量
Variable SDK 参考
pm.iterationData
测试数据变量:
因为测试数据是单独管理的,暂不支持在脚本中直接设置测试数据变量,但是可以在脚本中访问测试数据变量,如下:
pm.iterationData.has(variableName:String):function → Boolean
: 检查是否存在某个测试数据变量。pm.iterationData.get(variableName:String):function → *
: get 单个测试数据变量。pm.iterationData.replaceIn(variableName:String):function
: 以真实的值替换字符串里的包含的动态变量,如{{variable_name}}
。pm.iterationData.toObject():function → Object
: 以对象形式获取所有测试数据变量。
pm.environment
环境变量:
pm.environment.name:String
: 环境名。pm.environment.has(variableName:String):function → Boolean
:检查是否存在某个环境变量。pm.environment.get(variableName:String):function → *
:get 单个环境变量。pm.environment.set(variableName:String, variableValue:String):function
:set 单个环境变量。pm.environment.replaceIn(variableName:String):function
:以真实的值替换字符串里的包含的动态变量,如{{variable_name}}
。pm.environment.toObject():function → Object
:以对象形式获取当前环境的所有变量。pm.environment.unset(variableName:String):function
: unset 单个环境变量。pm.environment.clear():function
:清空当前环境的所有变量。
注意:以上所有操作都是读写的本地值,而不会读写远程值。
pm.globals
全局变量:
pm.globals.has(variableName:String):function → Boolean
:检查是否存在某个全局变量。pm.globals.get(variableName:String):function → *
:get 单个全局变量。pm.globals.set(variableName:String, variableValue:String):function
:set 单个全局变量。pm.globals.replaceIn(variableName:String):function
:以真实的值替换字符串里的包含的动态变量,如{{variable_name}}
。> 如前置脚本,获取请求参数的值如果包含变量,则需要使用 >> pm.globals.replaceIn>
> 才能将变量替换会真正的值。pm.globals.toObject():function → Object
:以对象形式获取所有全局变量。pm.globals.unset(variableName:String):function
: unset 单个全局变量。pm.globals.clear():function
:清空当前环境的全局变量。
注意:以上所有操作都是读写的本地值,而不会读写远程值。
pm.request
pm.request
: Request SDK 参考
request
是接口请求对象。在前置脚本中表示将要发送的请求,在后置脚本中表示已经发送了的请求。
request
包含了以下结构:
pm.request.url
:Url: 当前请求的 URL。pm.request.getBaseUrl()
:获取当前运行环境选择的的前置 URL
,在 2.1.39 版本之后支持。pm.request.headers
:HeaderList:当前请求的 headers 列表。pm.request.method
:String
当前请求的方法,如GET
、POST
等。pm.request.body
:RequestBody: 当前请求的 body 体。pm.request.headers.add({ key: headerName:String, value: headerValue:String})
:function
: 给当前请求添加一个 key 为headerName
的 header。pm.request.headers.remove(headerName:String)
:function
: 删除当前请求里 key 为headerName
的 headerpm.request.headers.get(headerName:String)
:function
: 查询当前请求里的headerName
。pm.request.headers.upsert({ key: headerName:String, value: headerValue:String})
:function
: upsert key 为headerName
的 header(如不存在则新增,如已存在则修改)。pm.request.auth
: 当前请求的身份验证信息
使用示例:
在后置操作中填入自定义脚本,参考下图示例,选择所需要的提取对象,编写对应的函数。
例如提取请求中的 headers 中的
Accept
值并打印到控制台。
pm.response
**以下部分 API 仅在
后置脚本
中可用**
pm.response:
Response SDK 参考
在后置脚本中
pm.response
接口请求完成后返回的 response 信息。
response 包含了以下结构:
pm.response.code:Number
pm.response.status:String
pm.response.headers:
HeaderListpm.response.responseTime:Number
pm.response.responseSize:Number
pm.response.text():Function → String
pm.response.json():Function → Object
pm.response.setBody('')
pm.response.headers.get
:在后置脚本中使用pm.response.headers.get
命令可以提取返回响应中的headers
中的值。例如想要在控制台中显示 Header 中的 Date 值,那么可以在后置操作中填写如下自定义脚本:var test = pm.response.headers.get("Date")console.log(test)
若希望将其作为变量供其它接口调用,详细说明请参考《最佳实践:接口之间如何传递数据?》
pm.cookies
pm.cookies:
CookieList SDK 参考
cookies
为当前请求对应域名下的 cookie 列表。
pm.cookies.has(cookieName:String):Function → Boolean
检查是否存在名为cookieName
的 cookie 值pm.cookies.get(cookieName:String):Function → String
get 名为cookieName
的 cookie 值pm.cookies.toObject:Function → Object
以对象形式获取当前域名下所有 cookiepm.cookies.jar().clear(pm.request.getBaseUrl())
清空全局 cookies
注意:pm.cookies 为接口请求后返回的 cookie,而不是接口请求发出去的 cookie。
pm.test
pm.test(testName:String, specFunction:Function):Function
该方法用来断言某个结果是否符合预期。- 以下示例为检查返回的 respone 是否正确:pm.test("response should be okay to process",function(){ pm.response.to.not.be.error; pm.response.to.have.jsonBody(""); pm.response.to.not.have.jsonBody("error");});
- 通过 callback 的可选参数done
,还可用来测试异步方法:pm.test("async test",function(done){setTimeout(()=>{ pm.expect(pm.response.code).to.equal(200);done();},1500);});
pm.test.index():Function → Number
从特定位置获取测试总数
pm.expect
pm.expect(assertion:*):Function → Assertion
pm.expect
是一个普通的断言方法,查看详细的说明:ChaiJS expect BDD library。
该方法用来断言
response
或
variables
里的数据非常有用。
更多关于
pm.expect
断言的是示例,查看:Assertion library examples
Response 对象可用的断言 API 列表
pm.response.to.have.status(code:Number)
pm.response.to.have.status(reason:String)
pm.response.to.have.header(key:String)
pm.response.to.have.header(key:String, optionalValue:String)
pm.response.to.have.body()
pm.response.to.have.body(optionalValue:String)
pm.response.to.have.body(optionalValue:RegExp)
pm.response.to.have.jsonBody()
pm.response.to.have.jsonBody(optionalExpectEqual:Object)
pm.response.to.have.jsonBody(optionalExpectPath:String)
pm.response.to.have.jsonBody(optionalExpectPath:String, optionalValue:*)
pm.response.to.have.jsonSchema(schema:Object)
pm.response.to.have.jsonSchema(schema:Object, ajvOptions:Object)
pm.response.to.be
pm.response.to.be
是用来快速断言的一系列内置规则。
pm.response.to.be.info
检查状态码是否为1XX
pm.response.to.be.success
检查状态码是否为2XX
pm.response.to.be.redirection
检查状态码是否为3XX
pm.response.to.be.clientError
检查状态码是否为4XX
pm.response.to.be.serverError
检查状态码是否为5XX
pm.response.to.be.error
检查状态码是否为4XX
或5XX
pm.response.to.be.ok
检查状态码是否为200
pm.response.to.be.accepted
检查状态码是否为202
pm.response.to.be.badRequest
检查状态码是否为400
pm.response.to.be.unauthorized
检查状态码是否为401
pm.response.to.be.forbidden
检查状态码是否为403
pm.response.to.be.notFound
检查状态码是否为404
pm.response.to.be.rateLimited
检查状态码是否为429
使用 JS 类库
内置类库列表
- Encode、Decode 库- crypto-js(v3.1.9-1):编码 / 解码库,常用的编码解码方式基本都有,如 Base64、MD5、SHA、HMAC、AES 等等。- 注意:只能 require 整个模块,不能单独 require 类库里的某个子模块。- atob(v2.1.2):Base64 解码- btoa(v1.2.1):Base64 编码- tv4(v1.3.0):JSONSchema 校验库- xml2js(v0.4.19):XML 转 JSON
- Encode、Decode 库- jsrsasign(10.3.0):RSA 加密 / 解密 (Apifox 版本号 >= 1.4.5 才支持,老版本不支持)
- 断言- chai (v4.2.0):BDD / TDD 断言库
- 实用工具- postman-collection( v3.4.0):Postman Collection 库- cheerio(v0.22.0):jQuery 的一个子集- lodash (v4.17.11):JS 实用工具库- moment(v2.22.2):日期处理库 (不含 locales)- uuid :生成 UUID- csv-parse/lib/sync( v1.2.4):CSV 格式数据处理- iconv-lite:用于字符编码之间的转换,支持数十种字符编码格式的转换。- mockjs:生成随机数据,拦截 Ajax 请求。
- JSONSchema 校验库- ajv(v6.6.2):JSONSchema 校验库
- 内置 NodeJS 模块- path- assert- buffer- util- url- punycode- querystring- string-decoder- stream- timers- events
通过
require
可以加载并使用 Apifox 内置的 JS 类库(也可以直接使用,但这样编写脚本时可能缺乏一些灵活性,具体选择取决于业务需求)。
// 引入 CryptoJS 库(可省略)const CryptoJS =require('crypto-js');
console.log(CryptoJS.SHA256("Message"));
内置类库使用示例
使用内置类库加密、解密、编码、解码数据的常见示例参考。
SHA256 加密
// SHA256 加密,输出 Base64// 定义要加密的消息const message ="Hello, World!";// 使用 SHA256 算法进行加密const hash = CryptoJS.SHA256(message);// 将加密结果输出为 Base64 编码const base64Encoded = CryptoJS.enc.Base64.stringify(hash);// 输出结果
console.log("SHA256: "+ base64Encoded);
HMAC-SHA256 加密
// HMAC-SHA256 加密,输出 Base64// 定义要加密的消息和密钥const message ="Hello, World!";const secretKey ="MySecretKey";// 使用 HMAC-SHA256 算法进行加密const hash = CryptoJS.HmacSHA256(message, secretKey);// 将加密结果输出为 Base64 编码const base64Encoded = CryptoJS.enc.Base64.stringify(hash);// 输出结果
console.log("HMAC-SHA256: "+ base64Encoded);
Base64 编码/解码
Base64 编码
// 要编码的消息const message ="你好,Apifox!";// 使用 CryptoJS 进行 Base64 编码const wordArray = CryptoJS.enc.Utf8.parse(message);const base64Encoded = CryptoJS.enc.Base64.stringify(wordArray);// 输出编码结果
console.log("Base64: "+ base64Encoded);
Base64 解码
- 字符串解码:
// Base64 编码的字符串(一般从响应数据中提取)let encodedData ={"data":"5L2g5aW977yMQXBpZm94IQ=="}// 解码 Base64 编码的数据let decodedData = CryptoJS.enc.Base64.parse(encodedData.data).toString(CryptoJS.enc.Utf8);// 输出解码后的结果console.log(decodedData);// "你好,Apifox!"
- JSON 解码:可通过
pm.response.setBody()
方法将解码后的 JSON 数据设置为响应 Body。// 引入 CryptoJS 库const CryptoJS =require("crypto-js");// 从响应中获取 Base64 编码的字符串let encodedData = pm.response.text();// 解码 Base64 编码的数据let decodedData = CryptoJS.enc.Base64.parse(encodedData).toString(CryptoJS.enc.Utf8);// 解析解码后的 JSON 字符串let jsonData =JSON.parse(decodedData);// 将解析后的 JSON 数据设置为响应体pm.response.setBody(jsonData);// 输出结果console.log(jsonData);
AES 加密/解密
AES 加密
// 引入 CryptoJS 库const CryptoJS =require("crypto-js");// 假设这是要加密的`password`字段的值,从环境变量中获取const password = pm.environment.get("password");// 使用安全的密钥和 IV(这里为了示例简化说明,实际应用中需要保护好这些敏感信息)const key = CryptoJS.enc.Utf8.parse('mySecretKey12345');// 确保是 16/24/32 字节const iv = CryptoJS.enc.Utf8.parse('myIVmyIVmyIVmyIV');// 确保是 16 字节// AES加密const encrypted = CryptoJS.AES.encrypt(password, key,{iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7
}).toString();// 把加密后的密码设置为一个新的变量,可以在请求体内使用
pm.environment.set("encryptedPassword", encrypted);
AES 解密
假设有一个经过 AES 加密的密文,其加密模式为 ECB,填充模式为 Pkcs7。其 AES 解密脚本示例如下:
// 引入 CryptoJS 库const CryptoJS =require('crypto-js');// 经过 Base64 编码的 AES 加密后的密文(一般从响应数据中提取)const ciphertext ="Gig+YJFu4fLrrexzam/vblRV3hoT25hPZn0HoNoosHQ=";// 解密所需密钥,确保是 16/24/32 字节(一般从环境变量中读取)const key = CryptoJS.enc.Utf8.parse('1234567891234567');// AES 解密const decryptedBytes = CryptoJS.AES.decrypt(ciphertext, key,{mode: CryptoJS.mode.ECB,// 解密模式padding: CryptoJS.pad.Pkcs7 // 填充方式});// 将解密后的字节数组转换为 UTF-8 字符串const originalText = decryptedBytes.toString(CryptoJS.enc.Utf8);// 输出解密后的文本
console.log(originalText);// "你好,Apifox!"
RSA 加密/解密
RSA 加密
// 引入 jsrsasign 库const jsrsasign =require('jsrsasign');// 定义公钥(一般从环境变量中读取)const publicKey =`
-----BEGIN PUBLIC KEY-----
公钥……
-----END PUBLIC KEY-----
`;// 用公钥加密const plaintext ="你好,Apifox!";const pubKeyObj = jsrsasign.KEYUTIL.getKey(publicKey);const encryptedHex = jsrsasign.KJUR.crypto.Cipher.encrypt(plaintext, pubKeyObj);
console.log("加密密文:", encryptedHex);
RSA 解密
// 引入 jsrsasign 库const jsrsasign =require('jsrsasign');// 定义私钥(一般从环境变量中读取)const privateKeyPEM =`
-----BEGIN PRIVATE KEY-----
私钥……
-----END PRIVATE KEY-----
`;// 定义密文(一般从响应数据中提取)const ciphertext ='';// 解密const prvKeyObj = jsrsasign.KEYUTIL.getKey(privateKeyPEM);const decrypted = jsrsasign.KJUR.crypto.Cipher.decrypt(ciphertext, prvKeyObj);
console.log(decrypted);
一个简单的 RSA 加密解密的完整示例参考 (注意 jsrsasign 版本为 10.3.0,其它版本语法可能会不兼容),可以将其在 Node.js 环境下运行,并根据需要在 Apifox 中执行加密或解密的操作:
const rsa =require('jsrsasign');// 生成 RSA 密钥对const keypair = rsa.KEYUTIL.generateKeypair("RSA",2048);const publicKey = rsa.KEYUTIL.getPEM(keypair.pubKeyObj);const privateKey = rsa.KEYUTIL.getPEM(keypair.prvKeyObj,"PKCS8PRV");
console.log("公钥:", publicKey);
console.log("私钥:", privateKey);// 用公钥加密const plaintext ="你好,Apifox!";const pubKeyObj = rsa.KEYUTIL.getKey(publicKey);const encryptedHex = rsa.KJUR.crypto.Cipher.encrypt(plaintext, pubKeyObj);
console.log("加密密钥:", encryptedHex);// 用私钥解密const prvKeyObj = rsa.KEYUTIL.getKey(privateKey);const decrypted = rsa.KJUR.crypto.Cipher.decrypt(encryptedHex, prvKeyObj);
console.log("解密明文:", decrypted);
非内置的 JS 类库
使用
fox.liveRequire
方法可以动态地引入从 npm 上发布的其他纯 JavaScript 库,以扩展 Apifox 的功能。请注意,仅支持在浏览器端运行的库,并且不支持带有 C/C++ 等语言扩展的库。如果尝试加载此类库,可能会导致运行超时或产生异常。为了最好的运行效果,请选择明确支持浏览器端运行的库,并仅引入纯 JavaScript 库。
注意:
- 仅 Apifox 版本号 >= 1.4.5 才支持,老版本不支持,请升级到最新版。
- 非内置库需要动态从网络下载 JS 类库,所以必须要联网,且性能有有所损耗,建议优先使用内置的 JS 库。
- WEB 版不支持该方式,请使用 Apifox 桌面客户端。
示例代码如下:
// 使用非内置的 JS 类库示例// 引入单个 npm 库:md5
fox.liveRequire("md5",(md5)=>{try{
console.log(md5("message"));// => '04a410d39d39f9831217edd702d7fde0'}catch(error){
console.error("An error occurred during liveRequire callback", error);throw error;}});// 引入多个 npm 库:camelize,md5
fox.liveRequire(["camelize","md5"],([camelize, md5])=>{try{
console.log("loaded module is ", camelize, md5);
console.log('camelize("foo-bar") is ',camelize("foo-bar"));// => 'fooBar'
console.log('md5("message") is ',md5("message"));// => '04a410d39d39f9831217edd702d7fde0'}catch(error){
console.error("An error occurred during liveRequire callback", error);throw error;}});// 引入多个 npm 库(带版本):camelcase,md5
fox.liveRequire([{name:"camelcase",version:"6.2.1",},"md5",],([camelCase, md5])=>{try{
console.log("loaded module is ", camelCase, md5);
console.log('camelCase("foo-bar") is ',camelCase("foo-bar"));// => 'fooBar'
console.log('md5("message") is ',md5("message"));// => '04a410d39d39f9831217edd702d7fde0'}catch(error){
console.error("An error occurred during liveRequire callback", error);throw error;}});
脚本示例
脚本使用环境/全局/临时变量
- 环境变量
// 设置环境变量pm.environment.set('variable_key','variable_value');// 获取环境变量var variable_key = pm.environment.get('variable_key');// unset 环境变量pm.environment.unset('variable_key');
环境变量写入环境变量支持数组、对象、字符串等形式存储var array =[1,2,3,4];pm.environment.set('array',JSON.stringify(array));var obj ={a:[1,2,3,4],b:{c:'val'}};pm.environment.set('obj',JSON.stringify(obj));
读取的时候,需要使用JSON.parse
转换回来try{var array =JSON.parse(pm.environment.get('array'));var obj =JSON.parse(pm.environment.get('obj'));}catch(e){// 处理异常}
- 全局变量
// 设置全局变量pm.globals.set('variable_key','variable_value');// 获取全局变量var variable_key = pm.globals.get('variable_key');// unset 全局变量pm.globals.unset('variable_key');
- 临时变量
// 设置临时变量pm.variables.set('variable_key','variable_value');// 获取临时变量var variable_key = pm.variables.get('variable_key');// unset 临时变量pm.variables.unset('variable_key');
脚本读取/修改接口请求信息
脚本如何读取/修改接口请求信息主要使用
pm.request
。
注意
- 只有在前置脚本里修改请求信息才是有效的,在后置脚本里修改无效。
- 通过脚本取出来的接口参数,如果参数包含变量,变量是不会替换成对应的值。如想要获取替换后的值,可使用
pm.variables.replaceIn
方法处理:// pm.variables.replaceIn 处理参数里的变量var body = pm.variables.replaceIn(pm.request.body.raw);var jsonData =JSON.parse(body);
URL 相关信息
// 获取 url 对象var urlObj = pm.request.url;// 获取完整接口请求 URL,包含 query 参数var url = urlObj.toString();// 获取协议(http 或 https)var protocol = urlObj.protocol;// 获取 端口var port = urlObj.port;
Header 参数
获取 header 参数
// 获取 Header 参数对象var headers = pm.request.headers;// 获取 key 为 field1 的 header 参数的值var field1 = headers.get("field1");// 已键值对象方式获取所有 header 参数var headersObject = headers.toObject();// 遍历整个 header
headers.each((item)=>{
console.log(item.key);// 输出参数名
console.log(item.value);// 输出参数值});
修改 header 参数
// 获取 Header 参数对象var headers = pm.request.headers;// 增加 header 参数
headers.add({key:"field1",value:"value1",});// 修改 header 参数(如不存在则新增)
headers.upsert({key:"field2",value:"value2",});
Query 参数
获取 query 参数
// 获取 Query 参数对象var queryParams = pm.request.url.query;// 获取 key 为 field1 的 query 参数的值var field1 = queryParams.get("field1");// 已键值对象方式获取所有 query 参数var quertParamsObject = queryParams.toObject();// 遍历整个 query
queryParams.each((item)=>{
console.log(item.key);// 输出参数名
console.log(item.value);// 输出参数值});
修改 query 参数
// 获取 Query 参数对象var queryParams = pm.request.url.query;// 增加 query 参数
queryParams.add({key:"field1",value:"value1",});// 修改 query 参数(如不存在则新增)
queryParams.upsert({key:"field2",value:"value2",});
Body 参数
Body 参数来自
pm.request.body
,pm.request.body 是一个
RequestBody
实例。
注意
- 如需修改 Body 里的数据,推荐在 Body 里引用变量,然后在前置脚本里设置对应变量的值,即可达到修改的目的。
- Body 参数也支持直接修改(版本 >= 1.4.16+),使用方式如下:
var body = pm.request.body.toJSON();console.log("body 对象", body);var bodyStr = body.raw;console.log("body 字符串", bodyStr);var bodyJSON =JSON.parse(bodyStr);bodyJSON.id =100;pm.request.body.update(JSON.stringify(bodyJSON,null,2));console.log("修改后 body", pm.request.body.toJSON());
body 类型为 form-data
获取 form-data 信息
// 当 body 类型为 form-data 时,从 pm.request.body.formdata 获取请求参数var formData = pm.request.body.formdata;// 获取 key 为 field1 的 form-data 参数的值var field1 = formData.get("field1");
console.log(field1);// 控制台打印 field1// 已键值对象方式获取所有 formdata 参数var formdataObject = formData.toObject();
console.log(formdataObject);// 控制台打印 formdataObject// 遍历整个 form-data 数据
formData.each((item)=>{
console.log(item.key);// 控制台打印参数名
console.log(item.value);// 控制台打印参数值});
设置 form-data 信息
注:可以使用该方法实现文件路径参数化
pm.request.body.update({mode:'formdata',formdata:[{key:'foo',value:'bar'}]});
body 类型为 x-www-form-urlencode
获取 x-www-form-urlencode 信息
// 当 body 类型为 x-www-form-urlencode** 时,从 pm.request.body.urlencoded 获取请求参数var formData = pm.request.body.urlencoded;// 获取 key 为 field1 的 form-data 参数的值var field1 = formData.get("field1");// 已键值对象方式获取所有 formdata 参数var formdataObject = formData.toObject();// 遍历整个 form 数据
formData.each((item)=>{
console.log(item.key);// 控制台打印参数名
console.log(item.value);// 控制台打印参数值});
设置 x-www-form-urlencode 信息
pm.request.body.update({mode:'urlencoded',urlencoded:[{key:'foo',value:'bar'}]});
body 类型为 json
获取 json 信息
// 当 body 类型为 json 时,从 pm.request.body.raw 获取请求参数try{var jsonData =JSON.parse(pm.request.body.raw);
console.log(jsonData);// 控制台打印参整个 json 数据}catch(e){
console.log(e);}
body 类型为 raw
获取 raw 信息
// 当 body 类型为 raw 时,从 pm.request.body.raw 获取请求参数var raw = pm.request.body.raw;
console.log(raw);// 控制台打印参整个 raw 数据
设置 raw 信息
pm.request.body.update('Hello World!');
body 类型为 GraphQL
设置 GraphQL 信息
pm.request.body.update({mode:'graphql',graphql:{query:`
query Square($ten: Int!) {
square(n: $ten)
}
`,variables:{ten:10}}});
断言(测试脚本)
后置脚本是在请求发送完成后执行的代码片段。主要用来断言请求返回的结果是否正确、将请求返回的结果数据写入环境变量等。
Apifox 内置了
ChaiJS
作为断言库,以下是常用的断言测试脚本示例,但并非全部示例,更多用法请参考文档
- 断言请求返回的结果是否正确
// pm.response.to.have 示例pm.test('返回结果状态码为 200',function(){ pm.response.to.have.status(200);});// pm.expect() 示例pm.test('当前为正式环境',function(){ pm.expect(pm.environment.get('env')).to.equal('production');});// response assertions 示例pm.test('返回结果没有错误',function(){ pm.response.to.not.be.error; pm.response.to.have.jsonBody(''); pm.response.to.not.have.jsonBody('error');});// pm.response.to.be* 示例pm.test('返回结果没有错',function(){// assert that the status code is 200 pm.response.to.be.ok;// info, success, redirection, clientError, serverError, are other variants// assert that the response has a valid JSON body pm.response.to.be.withBody; pm.response.to.be.json;// this assertion also checks if a body exists, so the above check is not needed});
- 将请求返回的结果数据写入环境变量
// 获取 JSON 格式的请求返回数据var jsonData = pm.response.json();// 将 jsonData.token 的值写入环境变量pm.environment.set('token', jsonData.token);
- 检查 response body 是否包含某个字符串
pm.test('Body matches string',function(){ pm.expect(pm.response.text()).to.include('string_you_want_to_search');});
- 检查 response body 是否包含等于字符串
pm.test('Body is correct',function(){ pm.response.to.have.body('response_body_string');});
- 检查 json 值
pm.test('Your test name',function(){var jsonData = pm.response.json(); pm.expect(jsonData.value).to.eql(100);});
- 检查 header 是否有设置 Content-Type
pm.test('Content-Type header is present',function(){ pm.response.to.have.header('Content-Type');});
- 检查请求响应耗时是否低于 200 毫秒
pm.test('Response time is less than 200ms',function(){ pm.expect(pm.response.responseTime).to.be.below(200);});
- 检查 HTTP 状态码是否为 200
pm.test('Status code is 200',function(){ pm.response.to.have.status(200);});
- 检查 HTTP 状态码名称是否包含某个字符串
pm.test('Status code name has string',function(){ pm.response.to.have.status('Created');});
- 是否正确的 POST 请求状态码
pm.test('Successful POST request',function(){ pm.expect(pm.response.code).to.be.oneOf([201,202]);});
断言(后置操作)
“后置操作”支持添加断言,你可以对接口返回的数据或响应时间设置断言,以判断当前接口返回是否符合预期。
设置断言
打开 Apifox 中的某条接口,在“后置操作”页中设置断言。例如输入
$.data.status
。
提示
根对象使用
$
符号进行表示,而无需区分是对象还是数组。
查看结果
运行后即可查看断言结果:
主要参考
- 官方文档
版权归原作者 墨鸦_Cormorant 所有, 如有侵权,请联系我们删除。