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,点击跳转

相关推荐
民乐团扒谱机11 分钟前
【微实验】谐波乘积谱(HPS)算法深度解析:原理、数学与代码实现
开发语言·人工智能·python·算法·语音识别·音乐
小白勇闯网安圈39 分钟前
Django Ajax、批量操作与分页实践
python·django
Logintern091 小时前
Langgraph使用MemorySaver建立有记忆的图
开发语言·windows·python
深念Y1 小时前
Opencode Event 表写入优化方案
数据库·人工智能·ai·node.js·bug·优化·opencode
Uncommon.1 小时前
使用pandas处理csv并转为张量
pytorch·python·深度学习·pandas
上玄code2 小时前
【Agent精讲】调一个LLMAPI背后的工程问题
java·开发语言·python·chatgpt
Uncommon.2 小时前
使用Pytorch操作张量(多维数组)
人工智能·pytorch·python
码云骑士2 小时前
105-Ollama本地部署-Llama3-Qwen2-Modelfile-REST-API
python
晚安code2 小时前
MCP Server 开发入门:手把手写一个能跑的 Server,三种协议怎么选
python·ai编程
CTA量化套保2 小时前
量化脚本准备实盘了吗?TqSdk 上线前工程检查
人工智能·python