Vue+Node调用Python并将结果显示到Web页面中

前言

关于这个知识点,其实很早之前我就想写一篇文章记录一下遇见的一些问题了,但是由于各种问题,一直没有来得及写,最近时间相对充裕,对我在学习过程中遇见的问题进行记录一下。

关于我为什么要这么来回折腾?如果你看过我以前的文章,可以很明显的看出我是一个纯前端,对于后端语言并不会,因此如果让我使用Java去调用Python,肯定花的时间会更多,因此我通过Node,做了一个调用Python的代码程序。

这个其实并不是很难,只要用过Node开发的,都可以做到。我这篇仅记录我从零接触,在实现过程中遇见的一些问题,并不做深入的讲解。

实现了什么

在Vue项目中写一个按钮,当用户点击按钮的时候,使用JavaScript调用Node的接口,通过新建子进程的方式,调用Python的代码块,如下:

实现过程

前端Web

  1. 首先我们需要创建一个Vue3+Vite的前端工程;
  2. 创建页面,支持用户触发事件,以调用Node接口驱动Python;

下面我们先来准备接口请求需要的环境

  • 通过npm install axios安装插件;

  • 在/public目录下创建config.js,用来存储接口请求路径(避免多个地址导致需要创建多个请求文件);

    js 复制代码
    const QUERY_BASE_URL = "http://127.0.0.1:3000"; // 接口请求基础地址
    window.config = {
      baseUrl: QUERY_BASE_URL,
    };
  • 在/public目录下创建server.js,用来将配置暴露到引入文件中;

    js 复制代码
    export const data = {
      baseUrl: window.config.baseUrl,
    };
  • 创建request.js,方便接口调用;

    js 复制代码
    import axios from "axios";
    const service = axios.create({
      timeout: 120000,
      headers: {
        "Content-Type": "application/json",
      },
    });
    service.interceptors.request.use(
      (config) => {
        if (sessionStorage.getItem("token")) {
          const token = sessionStorage.getItem("token");
          config.headers["authorization"] = "Bearer " + token;
        }
        return config;
      },
      (error) => {
        Promise.reject(error);
      }
    );
    service.interceptors.response.use(
      (response) => {
        return response.data;
      },
      (error) => {
        if (error.response) {
          if (error.request.status == 0) {
            error.message = "服务器发生错误,请检查服务器。";
          }
        }
        return Promise.reject(error);
      }
    );
    export default service;
  • 创建接口请求api,下面的接口会在Node部分讲到;

    js 复制代码
    import request from "@/api/request";
    import { data } from "~/server"; // /public/server
    const baseUrl = data.baseUrl + ""; // 添加代理用的
    // 测试Get参数使用
    export function testNodeGet(param) {
      return request({
        url: baseUrl + "/nodeTestGet",
        method: "get",
        params: param,
      });
    }
    // 测试Post参数使用
    export function TestNodePost(data) {
      return request({
        url: baseUrl + "/nodeTestPost",
        method: "post",
        data,
      });
    }
    // 测试Python脚本使用
    export function testPyScript(param) {
      return request({
        url: baseUrl + "/py/script",
        method: "get",
        params: param,
      });
    }
  • 创建前端页面,调用接口;

    html 复制代码
    <template>
      <div>
        <button @click="runCommandGet">测试Get传参</button>
        <button @click="runCommandPost">测试Post传参</button>
        <button @click="runCommandPython">Python九九乘法表</button>
        <pre>{{ output }}</pre>
      </div>
    </template>
    
    <script>
    import { testNodeGet, TestNodePost, testPyScript } from "@/api/modules/node.js";
    export default {
      data() {
        return {
          output: "",
        };
      },
      methods: {
        async runCommandGet() {
          try {
            const params = { name: "张三" };
            const response = await testNodeGet(params);
            this.output = response.result;
          } catch (error) {
            this.output = "Error: " + error;
          }
        },
        async runCommandPost() {
          try {
            const params = { name: "张三", age: 28, type: "user" };
            const response = await TestNodePost(params);
            this.output = response.result;
          } catch (error) {
            this.output = "Error: " + error;
          }
        },
        async runCommandPython() {
          try {
            const params = {};
            const response = await testPyScript(params);
            this.output = response.result;
          } catch (error) {
            this.output = "Error: " + error;
          }
        },
      },
    };
    </script>
    
    <style scoped lang="scss">
    button {
      margin-right: 12px;
    }
    </style>

Node服务

  • 创建Node接口程序,我使用express来实现,由于我的Node程序与依赖安装在Vue工程里面,因此这里需要注意引用的方式,我这里使用的是import的方式,并没有使用require,具体两者有什么区别,可以看我之前写的文章:NodeJS模块化,里面介绍了二者在引用、导出、使用过程中的区别与需要注意的点。下面是使用express创建的一个简单的接口程序:

    js 复制代码
    import express from "express";
    const app = express();
    const port = 3000;
    app.get("/api/get", (req, res) => {
      res.send({ success: true, result: req.query, });
    });
    app.listen(port, () => {
      console.log(`Server is running on http://localhost:${port}`);
    });
  • 上面的案例中,使用get方式进行了接口的实现,然而post相比较,需要注意的就是,post通过body进行传参,因此我们在抓取用户传入参数的时候,就不能直接通过req.query进行抓取了,我们需要在上面代码的基础上,添加如下代码:

    js 复制代码
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));

    通过这段代码,将post的入参进行解码,使能够正常访问,接口的实现如下:

    js 复制代码
    app.post("/api/post", (req, res) => {
      res.send({ success: true, result: req.body, });
    });
  • 创建子线程,调用python脚本。

    • 使用child_process实现创建子线程

    • 但是在创建子线程使用spawn调用python的时候,由于我们文件夹结构的原因,无法正常访问到python文件,因此这里需要对文件夹结构进行格式化,又用到了path,代码如下:

      js 复制代码
      import express from "express";
      import { spawn } from "child_process";
      import path from "path";
      
      const app = express();
      const port = 3000;
      
      let urlArr = new URL(".", import.meta.url).pathname.split("/");
      const __dirname = urlArr.filter((o) => !!o).join("/");
      
      app.get("/py/script", async (req, res) => {
        const pythonProcess = await spawn("python", [
          path.resolve(__dirname, "../python/test.py"), // python执行脚本路径
          "getFunc", // python脚本中的函数名
          JSON.stringify(req.query), // 需要传入的参数,因为是数组,保持一致性
        ]);
        try {
          pythonProcess.stdout.on("data", (data) => {
            // 接口返回数据
            res.send({
              success: true,
              result: data.toString(),
            });
          });
        } catch (error) {
          pythonProcess.stderr.on("data", (data) => {
            res.status(500).send(data.toString());
          });
        }
      });
  • Node完整代码如下:

    js 复制代码
    import express from "express";
    import { spawn } from "child_process";
    import path from "path";
    
    const app = express();
    const port = 3000;
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    
    let urlArr = new URL(".", import.meta.url).pathname.split("/");
    const __dirname = urlArr.filter((o) => !!o).join("/");
    
    app.get("/nodeTestGet", (req, res) => {
      res.send({
        success: true,
        result: req.query,
      });
    });
    
    app.post("/nodeTestPost", (req, res) => {
      res.send({
        success: true,
        result: req.body,
      });
    });
    
    app.get("/py/script", async (req, res) => {
      const pythonProcess = await spawn("python", [
        path.resolve(__dirname, "../python/test.py"), // python执行脚本路径
        "getFunc", // python脚本中的函数名
        JSON.stringify(req.query), // 需要传入的参数,因为是数组,保持一致性
      ]);
      try {
        pythonProcess.stdout.on("data", (data) => {
          res.send({
            success: true,
            result: data.toString(),
          });
        });
      } catch (error) {
        pythonProcess.stderr.on("data", (data) => {
          res.status(500).send(data.toString());
        });
      }
    });
    
    app.listen(port, () => {
      console.log(`Server is running on http://localhost:${port}`);
    });

Python脚本

这里只需要注意一个点,python需要返回到接口的数据,直接print即可,如果print多个,会拼接返回。

python 复制代码
import sys
import json

def getFunc():
    for i in range(1, 10):
        for j in range(1, i+1):
            print(f"{j} x {i} = {i*j}", end="\t")
        print()

def getHello(params): # params是传入的参数,如果要返回给js模块,直接使用print输出即可
    print("Hello, World!", 1+1)

if __name__ == "__main__":
    func_name = sys.argv[1] # Node中调用python的第二个参数
    args = sys.argv[2] # Node中调用python的第三个参数
    if func_name == "getFunc":
        getFunc()
    elif func_name == "getHello":
        params = json.loads(args)  # 解析 JSON 字符串
        getHello(params)

代码仓库

本文相关代码已上传至gitee,点击跳转

相关推荐
用户277844910499313 小时前
借助DeepSeek智能生成测试用例:从提示词到Excel表格的全流程实践
人工智能·python
JavaEdge在掘金15 小时前
ssl.SSLCertVerificationError报错解决方案
python
我不会编程55515 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
Mintopia16 小时前
Node.js 中 fs.readFile API 的使用详解
前端·javascript·node.js
老歌老听老掉牙16 小时前
平面旋转与交线投影夹角计算
python·线性代数·平面·sympy
ElasticPDF-新国产PDF编辑器16 小时前
Vue use pdf.js and Elasticpdf tutorial
vue.js·pdf
满怀101516 小时前
Python入门(7):模块
python
无名之逆16 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
你觉得20516 小时前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义下载方法
大数据·人工智能·python·gpt·学习·机器学习·aigc
啊喜拔牙16 小时前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala