当前位置:   article > 正文

Spring Boot动态api执行python脚本_springboot调用python接口

springboot调用python接口

一、背景

现状:

1、每次客户有需求,我们都需要在系统中新增接口,然后再将系统重新进行发布。

2、系统中存在很多的基本接口,大部分数据都能通过这些基本接口进行调用拼接。

因此,基于以上两点现状,领导提出以下要求:

1、系统能够动态新增接口,接口返回数据可以由系统已有基本接口进行调用拼接处理,而且系统不能够重新发布。

2、调用基本接口及拼接数据的流程在python脚本中完成,也就是一个接口对应一个脚本文件,能够将python脚本文件执行的结果返回给用户。(至于使用python脚本的原因主要是考虑外部团队使用python居多,既然未来动态api系统可能开发给他们,那么选择使用python最好不过,实际情况根据大家各自情况进行选择)。

3、能够向脚本文件中传入外部参数。

解决方案:

1、动态新增api接口时(并非实际在系统中新增一个接口,而是通过路径进行动态匹配),绑定python脚本文件,新增成功返回调用接口码。

2、根据固定调用地址+接口码进行接口调用,如果接口存在,则调用,不存在则结束。

3、用户调用接口之后,将找到对应的脚本文件进行调用,将调用结果返回给用户。

二、实现过程

1、创建一个实体类,用于接收用户提交的创建接口相关参数

  1. /**
  2. * api实体类(页面提交)
  3. */
  4. @Data
  5. @ToString
  6. @NoArgsConstructor
  7. @AllArgsConstructor
  8. @Accessors(chain = true)
  9. public class ApiVO {
  10. private String name;
  11. private String file;
  12. }

2、创建一个Map存储工具类,用于模拟数据库插入获取数据(懒得创建表和写SQL之类的了)

  1. @Component
  2. public class AccessUtil {
  3. private final Map<String, String> urls = new ConcurrentHashMap<String, String>();
  4. public void putUrls(String code, String filePath){
  5. urls.put(code, filePath);
  6. }
  7. public String getValue(String code){
  8. return urls.get(code);
  9. }
  10. }

3、创建一个api新增接口,接口创建成功将返回唯一的一串字符码

  1. /**
  2. * api管理类
  3. */
  4. @RequestMapping("/dynamics-api")
  5. @RestController
  6. public class ApiController {
  7. @Resource
  8. private AccessUtil accessUtil;
  9. /**
  10. * 动态新增一个api接口, 并非实际创建出一个接口,而是根据路径上的{xxx}进行接口匹配
  11. * @param apiVO 新增接口对象
  12. */
  13. @PostMapping("/one")
  14. public String addApi(@RequestBody ApiVO apiVO){
  15. String apiName = apiVO.getName();
  16. // 这里实际需要上传一个脚本文件,为了省事,我先准备好了文件,这里通过上传脚本名称替代上传文件
  17. String file= apiVO.getFile();
  18. // code为生成的接口调用地址的一部分,唯一
  19. String code = DigestUtils.md5DigestAsHex((apiName + file).getBytes());
  20. // 模拟生成了一个接口
  21. accessUtil.putUrls(code, file);
  22. return code;
  23. }
  24. }

4、创建一个通过接口调用适配接口,所有新增的接口,将通过以下适配接口进行匹配调用

  1. @Slf4j
  2. @RequestMapping("/adapter")
  3. @RestController
  4. public class AdapterController {
  5. @Resource
  6. private AccessUtil accessUtil;
  7. @GetMapping("/execute/{code}")
  8. public String executeApi(@PathVariable("code") String code) throws Exception{
  9. String value = accessUtil.getValue(code);
  10. if(value == null){
  11. log.error("接口不存在");
  12. return null;
  13. }
  14. // 拿到python脚本文件
  15. String scriptPath = "D:\\test\\" + value;
  16. File file = new File(scriptPath);
  17. if(!file.exists() || !file.isFile()){
  18. log.error("脚本文件不存在");
  19. return null;
  20. }
  21. // 随便制造点参数,模拟向脚本中传递参数
  22. Map<String, String> paramsMap = new HashMap<>();
  23. paramsMap.put("param1", "value1");
  24. paramsMap.put("params2", "value2");
  25. // 拼接参数,以key1@@#@@value1##@##key2@@#@@value2的方式
  26. String lineSplit = "##@##";
  27. String valueSplit = "@@#@@";
  28. StringJoiner paramsStr = new StringJoiner(lineSplit);
  29. for(String key : paramsMap.keySet()) {
  30. paramsStr.add(key + valueSplit + paramsMap.get(key));
  31. }
  32. // 执行脚本
  33. ProcessBuilder processBuilder;
  34. // 没有配置virtualenv环境,直接运行,前提是本地安装了python
  35. processBuilder = new ProcessBuilder("python", file.getPath(), paramsStr.toString());
  36. processBuilder.redirectErrorStream(true);
  37. Process process = processBuilder.start();
  38. // 处理结果,每行以#@#@#进行分割
  39. InputStreamReader ir = new InputStreamReader(process.getInputStream());
  40. BufferedReader br = new BufferedReader(ir);
  41. String line;
  42. StringJoiner result = new StringJoiner("#@#@#");
  43. while ((line = br.readLine()) != null) {
  44. log.warn(line);
  45. result.add(line);
  46. }
  47. br.close();
  48. ir.close();
  49. process.waitFor();
  50. // 打印结果
  51. return result.toString();
  52. }
  53. }

5、python脚本内容如下

  1. # coding:utf-8
  2. import sys
  3. import json
  4. import ssl
  5. import base64
  6. import requests
  7. import time
  8. # 命令参数=========================================================================
  9. COMMAND_PARAMS = {}
  10. def getData():
  11. return [
  12. {
  13. "name": "zhangsan",
  14. "phone" "12345678910",
  15. "address": "x-y-z-santi"
  16. }
  17. ]
  18. def getCommandParams():
  19. return COMMAND_PARAMS
  20. def main():
  21. # 用户的主要编辑区域,返回变量data为最终想要的结果
  22. data = getData()
  23. commandParams = getCommandParams()
  24. return data
  25. #获取传入参数集合,参数是以如下形式传入: key1@@#@@value1##@##key2@@#@@value2
  26. paramList = sys.argv[1].split("##@##")
  27. for num in paramList:
  28. COMMAND_PARAMS[num.split("@@#@@")[0]] = num.split("@@#@@")[1]
  29. # 执行函数
  30. data = main()
  31. # 接口获取数据通过print()得到
  32. print(COMMAND_PARAMS)
  33. 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接口执行脚本具体实现流程,如果觉得对你有所有帮助,请点个赞或者收藏支持一下吧!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/201089
推荐阅读
相关标签
  

闽ICP备14008679号