前置操作和后置操作都是 API 请求在发送和响应过程中执行的脚本,主要用于在发起 API 请求前和获得响应后完成验证或执行某些操作,目的是为了提高 API 调试和测试的效率,并确保接口的正确性。
前置操作是在 API 请求之前执行的脚本代码,可以用于做以下事情:
设置 API header(请求头)
它们可用于设置请求的请求头、请求正文、验证请求参数和配置身份验证等。
设置 API 参数
前置操作能够访问环境变量、全局变量和请求变量中的数据。前置操作也可以帮助请求者了解请求参数以及如何处理它们。
添加身份验证
例如基本身份验证或 OAuth。在发送 API 请求之前,前置操作可以被用来获取访问令牌或者其他权限,确保 API 请求发送的是有效的和合法的请求。
“变量替换”功能通常用于在发送 API 请求前,作用是把接口请求参数里的所有的已引用变量(包括动态值)替换成真实的请求内容。通常用于处理接口签名等转换场景。此时主要涉及以下两种场景:
通过脚本 set 变量
该场景下的操作需要放在“变量替换”之前运行,否则通过该脚本 set 的变量就不会对当前接口的请求参数生效。
接口签名脚本
该场景下的脚本需放在“变量替换”之后,这样脚本才能获取到该接口实际请求的参数值,否则获取到的参数值是变量替换之前的模板数据。
如需了解更多,请参考《接口签名如何处理?》
后置操作能够利用获取来自接口响应的数据,例如状态代码、header、body 等信息,进行二次处理:
前后置操作都可以在接口目录中设置父级操作。父级操作可以被继承到该目录下的所有接口中,适用于需要在多个接口中执行相同的前置操作的场景,例如鉴权,变量替换等。 接口本身可以灵活调整各操作的运行顺序。
子级接口可以选择是否采用父级操作。
当子级接口下又存在多个接口时,若选择关闭引用,次级接口将默认关闭该自定义脚本。
在一些具有明显上下游关系的接口中,有时需要将 A 接口的返回数据作为 B 接口的请求参数。
比如在创建宠物信息场景下,需要将 A 接口返回的 pet_id
中的数据作为后续接口的请求参数,然后在 B 接口中的「后置操作」中添加「提取变量」功能,基于 A 接口返回的结果自动提取数据并设置为变量(临时变量/环境变量/全局变量),方便其它接口运行的时候直接使用。
打开 Apifox 中的某条接口,在**“后置操作”页中添加“提取变量”,变量类型选择为“环境变量”**。
例如当前 B 接口的请求参数依赖于 A 接口返回的数据,现希望 B 接口在发送请求的同时能自动获取 A 接口返回的数据并作为请求参数。实现思路如下:
打开 A 接口用例的”后置操作“页,在后置操作中添加提取变量功能。将接口返回 Response JSON 数据里的 token 值提取到名为 petId
的变量中。
在 B 接口中的请求参数中直接填写 {{petId}}
,即可在请求中引用上一步骤中所创建的数值。
若不确定是否正确引用了 A 接口所生成的数据,可以在 B 接口的前置操作中添加以下自定义脚本:
var petId = pm.environment.get("petId"); console.log(petId)
这样就可以在控制台中查看已引用的变量值。
JSONPath 之于 JSON,就如 XPath 之于 XML。JSONPath 可以方便对 JSON 数据结构进行内容提取。
如果对 JSON Path 不熟悉,推荐使用这个免费的 AI 工具来生成:https://app.anakin.ai/apps/21854
根对象使用 $
来表示,而无需区分是对象还是数组
表达式可以使用.
,也可以使用[]
。如:
$.store.book[0].title
或 $['store']['book'][0]['title']
表达式(
可用作显式名称或索引的替代,如:
$.store.book[(@.length-1)].title
:表示获取最后一个 book 的 title
使用符号@
表示当前对象。过滤器表达式通过语法支持,?(
如:
$.store.book[?(@.price < 10)].title
:表示获取价格小于 10 的所有 book 的 title
要点:
$
表示文档的根元素@
表示文档的当前元素.node_name
或 ['node_name']
匹配下级节点[index]
检索数组中的元素[start:end:step]
支持数组切片语法*
作为通配符,匹配所有成员..
子递归通配符,匹配成员的所有子元素()
使用表达式?()
进行数据筛选JSONPath 语法和 XPath 对比:
XPath | JsonPath | 说明 |
---|---|---|
/ | $ | 文档根元素 |
. | @ | 当前元素 |
/ | . 或[] | 匹配下级元素 |
.. | N/A | 匹配上级元素,JsonPath 不支持此操作符 |
// | .. | 递归匹配所有子元素 |
* | * | 通配符,匹配下级元素 |
@ | N/A | 匹配属性,JsonPath 不支持此操作符 |
[] | [] | 下标运算符,根据索引获取元素,XPath 索引从 1 开始,JsonPath 索引从 0 开始 |
` | ` | [,] |
N/A | [start:end:step] | 数据切片操作,XPath 不支持 |
[] | ?() | 过滤表达式 |
N/A | () | 脚本表达式,使用底层脚本引擎,XPath 不支持 |
() | N/A | 分组,JsonPath 不支持 |
注意:
$.store.book[?(@.category=='reference')]
中的'reference'
下面是相应的 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 } } }
对这个文档进行解析:
XPath | JsonPath | Result |
---|---|---|
/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。
pm.response
对象。若在请求参数中包含当前时间戳,那么可以将函数返回值设置为环境变量。
将参数 timestamp 的值设置为 {{timestamp}}
。当请求发送时,前置脚本将被执行,环境变量 timestamp 的值会被设置为当前时间戳,同时 {{timestamp}}
也会被替换为当前时间戳。
若在请求头需要携带 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()); });
加密/解密
// 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);
pm.sendRequest
和 pm.environments.set
方法。变量示例
示例 A
// 声明局部变量,无法被其他脚本调用 var my_var = "hello";
将代码修改为以下格式:
// 声明全局变量,可以被其他脚本调用 my_var = "hello";
示例 B
// 声明局部方法,无法被其他脚本调用 function my_fun(name) { console.log("hello" + name); }
将代码修改为以下格式:
// 声明全局方法,可以被其他脚本调用 my_fun = function (name) { console.log("hello" + name); };
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: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 // 此处为 urlencoded urlencoded: [ { 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"); }); });
参考:
临时变量:
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.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.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.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
: 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
值并打印到控制台。
以下部分 API 仅在
后置脚本
中可用
pm.response:
Response SDK 参考
在后置脚本中 pm.response
接口请求完成后返回的 response 信息。
response 包含了以下结构:
pm.response.code:Number
pm.response.status:String
pm.response.headers:
HeaderList
pm.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:
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
以对象形式获取当前域名下所有 cookie
pm.cookies.jar().clear(pm.request.getBaseUrl())
清空全局 cookies
注意:pm.cookies 为接口请求后返回的 cookie,而不是接口请求发出去的 cookie。
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(assertion:*):Function → Assertion
pm.expect
是一个普通的断言方法,查看详细的说明:ChaiJS expect BDD library。
该方法用来断言 response
或 variables
里的数据非常有用。
更多关于 pm.expect
断言的是示例,查看:Assertion library examples
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.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
Encode、Decode 库
crypto-js
(v3.1.9-1):编码 / 解码库,常用的编码解码方式基本都有,如 Base64、MD5、SHA、HMAC、AES 等等。
atob(v2.1.2):Base64 解码
btoa(v1.2.1):Base64 编码
tv4(v1.3.0):JSONSchema 校验库
xml2js(v0.4.19):XML 转 JSON
Encode、Decode 库
断言
实用工具
JSONSchema 校验库
内置 NodeJS 模块
通过 require
可以加载并使用 Apifox 内置的 JS 类库(也可以直接使用,但这样编写脚本时可能缺乏一些灵活性,具体选择取决于业务需求)。
// 引入 CryptoJS 库(可省略) const CryptoJS = require('crypto-js'); console.log(CryptoJS.SHA256("Message"));
使用内置类库加密、解密、编码、解码数据的常见示例参考。
// 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 加密,输出 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 编码
// 要编码的消息 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 加密
// 引入 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 加密
// 引入 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);
使用 fox.liveRequire
方法可以动态地引入从 npm 上发布的其他纯 JavaScript 库,以扩展 Apifox 的功能。请注意,仅支持在浏览器端运行的库,并且不支持带有 C/C++ 等语言扩展的库。如果尝试加载此类库,可能会导致运行超时或产生异常。为了最好的运行效果,请选择明确支持浏览器端运行的库,并仅引入纯 JavaScript 库。
注意:
示例代码如下:
// 使用非内置的 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 对象 var urlObj = pm.request.url; // 获取完整接口请求 URL,包含 query 参数 var url = urlObj.toString(); // 获取协议(http 或 https) var protocol = urlObj.protocol; // 获取 端口 var port = urlObj.port;
获取 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 参数对象 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 参数来自 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());
获取 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' }] });
获取 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' }] });
获取 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); }
获取 raw 信息
// 当 body 类型为 raw 时,从 pm.request.body.raw 获取请求参数 var raw = pm.request.body.raw; console.log(raw); // 控制台打印参整个 raw 数据
设置 raw 信息
pm.request.body.update('Hello World!');
设置 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
。
提示
根对象使用 $
符号进行表示,而无需区分是对象还是数组。
运行后即可查看断言结果: