一、背景
现状:
1、每次客户有需求,我们都需要在系统中新增接口,然后再将系统重新进行发布。
2、系统中存在很多的基本接口,大部分数据都能通过这些基本接口进行调用拼接。
因此,基于以上两点现状,领导提出以下要求:
1、系统能够动态新增接口,接口返回数据可以由系统已有基本接口进行调用拼接处理,而且系统不能够重新发布。
2、调用基本接口及拼接数据的流程在python脚本中完成,也就是一个接口对应一个脚本文件,能够将python脚本文件执行的结果返回给用户。(至于使用python脚本的原因主要是考虑外部团队使用python居多,既然未来动态api系统可能开发给他们,那么选择使用python最好不过,实际情况根据大家各自情况进行选择)。
3、能够向脚本文件中传入外部参数。
解决方案:
1、动态新增api接口时(并非实际在系统中新增一个接口,而是通过路径进行动态匹配),绑定python脚本文件,新增成功返回调用接口码。
2、根据固定调用地址+接口码进行接口调用,如果接口存在,则调用,不存在则结束。
3、用户调用接口之后,将找到对应的脚本文件进行调用,将调用结果返回给用户。
二、实现过程
1、创建一个实体类,用于接收用户提交的创建接口相关参数
/**
* api实体类(页面提交)
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class ApiVO {
private String name;
private String file;
}
2、创建一个Map存储工具类,用于模拟数据库插入获取数据(懒得创建表和写SQL之类的了)
@Component
public class AccessUtil {
private final Map<String, String> urls = new ConcurrentHashMap<String, String>();
public void putUrls(String code, String filePath){
urls.put(code, filePath);
}
public String getValue(String code){
return urls.get(code);
}
}
3、创建一个api新增接口,接口创建成功将返回唯一的一串字符码
/**
* api管理类
*/
@RequestMapping("/dynamics-api")
@RestController
public class ApiController {
@Resource
private AccessUtil accessUtil;
/**
* 动态新增一个api接口, 并非实际创建出一个接口,而是根据路径上的{xxx}进行接口匹配
* @param apiVO 新增接口对象
*/
@PostMapping("/one")
public String addApi(@RequestBody ApiVO apiVO){
String apiName = apiVO.getName();
// 这里实际需要上传一个脚本文件,为了省事,我先准备好了文件,这里通过上传脚本名称替代上传文件
String file= apiVO.getFile();
// code为生成的接口调用地址的一部分,唯一
String code = DigestUtils.md5DigestAsHex((apiName + file).getBytes());
// 模拟生成了一个接口
accessUtil.putUrls(code, file);
return code;
}
}
4、创建一个通过接口调用适配接口,所有新增的接口,将通过以下适配接口进行匹配调用
@Slf4j
@RequestMapping("/adapter")
@RestController
public class AdapterController {
@Resource
private AccessUtil accessUtil;
@GetMapping("/execute/{code}")
public String executeApi(@PathVariable("code") String code) throws Exception{
String value = accessUtil.getValue(code);
if(value == null){
log.error("接口不存在");
return null;
}
// 拿到python脚本文件
String scriptPath = "D:\\test\\" + value;
File file = new File(scriptPath);
if(!file.exists() || !file.isFile()){
log.error("脚本文件不存在");
return null;
}
// 随便制造点参数,模拟向脚本中传递参数
Map<String, String> paramsMap = new HashMap<>();
paramsMap.put("param1", "value1");
paramsMap.put("params2", "value2");
// 拼接参数,以key1@@#@@value1##@##key2@@#@@value2的方式
String lineSplit = "##@##";
String valueSplit = "@@#@@";
StringJoiner paramsStr = new StringJoiner(lineSplit);
for(String key : paramsMap.keySet()) {
paramsStr.add(key + valueSplit + paramsMap.get(key));
}
// 执行脚本
ProcessBuilder processBuilder;
// 没有配置virtualenv环境,直接运行,前提是本地安装了python
processBuilder = new ProcessBuilder("python", file.getPath(), paramsStr.toString());
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
// 处理结果,每行以#@#@#进行分割
InputStreamReader ir = new InputStreamReader(process.getInputStream());
BufferedReader br = new BufferedReader(ir);
String line;
StringJoiner result = new StringJoiner("#@#@#");
while ((line = br.readLine()) != null) {
log.warn(line);
result.add(line);
}
br.close();
ir.close();
process.waitFor();
// 打印结果
return result.toString();
}
}
5、python脚本内容如下
# coding:utf-8
import sys
import json
import ssl
import base64
import requests
import time
# 命令参数=========================================================================
COMMAND_PARAMS = {}
def getData():
return [
{
"name": "zhangsan",
"phone" "12345678910",
"address": "x-y-z-santi"
}
]
def getCommandParams():
return COMMAND_PARAMS
def main():
# 用户的主要编辑区域,返回变量data为最终想要的结果
data = getData()
commandParams = getCommandParams()
return data
#获取传入参数集合,参数是以如下形式传入: key1@@#@@value1##@##key2@@#@@value2
paramList = sys.argv[1].split("##@##")
for num in paramList:
COMMAND_PARAMS[num.split("@@#@@")[0]] = num.split("@@#@@")[1]
# 执行函数
data = main()
# 接口获取数据通过print()得到
print(COMMAND_PARAMS)
print(data)
请根据实际情况写脚本,我这里随便写的
6、测试效果
调用新增结果,返回code
返回唯一码:调用脚本返回结果如下:
将code拼接在适配接口上进行调用
调用脚本返回结果如下:
三、拓展
以上是通过在本地的python环境中执行的,还有一种就是在沙箱环境中去执行,能够更加的安全,请参考以下博客:
https://blog.csdn.net/lmchhh/article/details/128021914
https://blog.csdn.net/lmchhh/article/details/128806208?spm=1001.2014.3001.5502
四、总结
以上就是新增api接口执行脚本具体实现流程,如果觉得对你有所有帮助,请点个赞或者收藏支持一下吧!