在一些特定场景下,比如需要在 Apifox 中对文件进行读写、加密、转换格式或者进行其它业务的操作时,仅使用 Apifox 内置的 JS 类库可能无法满足业务需求,这时,就可以借助「外部程序」作为解决方案。
外部程序是保存在「外部程序目录」下的代码文件,可以是 Java 程序归档文件 jar 包,也可以是其他程序的代码源文件,例如 .jar、.py、.php、.js 等后缀的文件。文件需要放到「外部程序目录」下,这个目录可以在页面右上角的「设置 -> 外部程序」中打开。
使用「外部程序」时需要注意两点:
- Apifox 版本必须是 2.3.25 或以上才支持调用外部程序。
- 外部程序是在「沙盒环境」以外运行的,这意味着它有权限访问你电脑上的其他程序、文件和数据,所以要确保外部程序是安全的。
调用「外部程序」的语法
在 Apifox 中调用外部程序可以使用 pm.executeAsync() 方法,其语法如下,可将其写在接口的「前置/后置操作」中:
await pm.executeAsync(filePath, args, options)
其内各项参数表示如下,更具体的可参考帮助文档里的解释。
- filePath (string) : 外部程序路径。
- args (string[]) : 传递给外部程序的参数,数组中每个字符串代表一个参数。其中,非 string 类型会隐式转换为 string 类型。
- options (Object) : 其他选项设置,包括:
-
- command (string) : 外部程序的执行命令。非必填,如果不填会自动推断。
- cwd (string) : 子进程工作目录。非必填,默认为「外部程序目录」。
- env (Record) : 子进程环境变量。非必填,默认为 {}。
- windowsEncoding (string) : Windows 系统用使用的编码格式。非必填,默认为 "cp936"。
- className (string) : 调用 jar 包中的类名,非必填,详见 jar 包中的方法。
- method (string) : 调用 jar 包中的方法名,当 className 有值时为必填项。
- paramTypes (string[]) : 指定 jar 包中方法参数的类型,默认根据参数自动推断。
在调用外部程序时,Apifox 会启动一个子进程,在里面以命令行的方式运行你指定的外部程序,然后,会把子进程的标准输出 (stdout) 作为外部程序的返回值。
这么说可能有点绕,简单讲就是 Apifox 会根据 pm.executeAsync() 方法里的参数生成一个命令,然后用命令去执行这个外部程序,并捕获输出的结果。例如:脚本 pm.executeAsync('add.py', ['2', '3']) 的实际执行命令为 python add.py 2 3。
举个更具体的例子。
假设你有一个 Python 脚本,用于计算两个数字的和。现在,你想在 Apifox 中调用这个外部的 Python 脚本,就可以使用 pm.executeAsync() 方法,具体如下。
准备外部程序
首先,你需要创建一个 Python 文件并将其放到「外部程序目录」下,例如 add.py,在脚本中,你一般需要处理传入的参数,并生成相应的输出。
# add.pyimport sys
# 从命令行参数中获取两个数字num1 = int(sys.argv[1])num2 = int(sys.argv[2])
# 计算两个数字的和result = num1 + num2
# 将结果打印到标准输出(stdout)print(result)
1 处理传入的参数
外部程序在 Apifox 中是以命令行的方式执行的,所以外部程序只能从命令行中获取到传入的参数。
上述脚本中的 sys.argv 是 Python 用于获取命令行参数的方法。例如使用 pm.executeAsync('add.py', [2, 3]) 调用外部脚本,其实际执行命令为 python add.py 2 3,则在外部脚本 add.py 中,获取参数的方式为:
# 从命令行参数中获取两个数字num1 = int(sys.argv[1]) // 2num2 = int(sys.argv[2]) // 3
每种编程语言获取命令行参数的方式与该语言的语法相关联,不同的编程语言会有不同的机制来处理命令行参数,举例来说:
- 在 Python 中,你可以使用 sys.argv 列表来访问命令行参数。
- 在 JavaScript 中,如果你在 Node.js 环境下运行脚本,你可以使用 process.argv 来获取命令行参数。
- 在 Java 中,命令行参数可以通过 args[] 参数传递给 main() 方法。
2 生成相应的输出
Apifox 不会捕获函数的返回值 (return) ,而是依赖于程序的标准输出 (stdout) 。
上述脚本里的 print() 作为 Python 的标准输出 (stdout) ,将计算结果输出到控制台或终端,Apifox 会捕获到这个输出结果,所以你将结果打印出来就可以将其返回。其它编程语言如 JavaScript 的 console.log()、Java 的 System.out.println() 也作为标准输出,具体取决于每种编程语言的语法。
外部程序建议先进行单独测试,确保其可以独立运行并产生预期结果,然后再在 Apifox 中调用,例如使用命令行 python add.py 2 3 运行这个文件,就会打印出结果。
其它类型的外部程序,比如要将一个图片文件转成 Base64、读取或保存图片文件、将 Word、PDF 等文件进行加密或者其它一系列你需要的操作,都建议先手动运行程序并检查输出。
在前置/后置操作中调用
外部程序准备好以后,你可以在 Apifox 的「前置/后置操作」中创建一个自定义脚本,然后使用 pm.executeAsync() 方法来调用「步骤一」中的外部程序。以下是一个示例:
// 使用 pm.executeAsync 调用 Python 脚本async function callPythonScript() { // 外部程序的路径为 add.py const scriptPath = 'add.py'; // 传递给 Python 脚本的参数,例如两个数字 2 和 3 const args = ['2', '3'];
try { // 调用 Python 脚本,并等待结果 const result = await pm.executeAsync(scriptPath, args); console.log('Result:', result); } catch (error) { console.error('Error:', error); }}
// 调用函数开始执行callPythonScript();
这个示例可以简化成下面这样的:
// 外部程序路径,传递参数const result = await pm.executeAsync('add.py', ['2', '3'])console.log('Result:', result);
传递的参数除了常量/固定值以外,你还可以通过变量的方式进行传递,例如:
// 读取环境变量的值const scriptPath = pm.environment.get('scriptPath')const arg1 = pm.environment.get('arg1')const arg2 = pm.environment.get('arg2')
const result = await pm.executeAsync(scriptPath, [arg1, arg2])console.log('Result:', result);
Apifox 会根据你提供的参数拼接出一个命令字符串,比如这个例子中 pm.executeAsync('add.py', ['2', '3']) 方法拼接成的命令就是:
python add.py 2 3
这跟你在本地运行这个 add.py 文件时使用的命令相一致。
当你在 Apifox 中发送请求时,会执行这个 JavaScript 脚本,并通过生成的命令调用外部程序 add.py,以获取外部程序的输出结果,如下图所示:
在 Apifox 中获取到的返回结果为 String 类型,你可根据实际需要进行类型转换。结果获取到之后,可将结果存储到环境变量中,以供取用,语法参考如下:
pm.environment.set('变量名', '变量值');
常见问题
1 macOS 提示找不到 Python 命令
在 macOS 系统上,默认安装了 Python 3 而不是 Python 2,要执行 Python 脚本,可使用 pm.executeAsync() 方法并设置参数 command 值为 "python3" 来确保使用正确的 Python 版本,例如:
await pm.executeAsync('demo.py', [], { command: 'python3' })
2 部分 Windows 系统返回中文乱码
可设置 windowsEncoding 参数值为 'utf8',例如:
await pm.executeAsync('demo.js', [], { windowsEncoding: 'utf8' })