Electron 接口请求全解析:从疑问到落地(真实开发对话整理)

0 阅读9分钟

在 Electron 开发中,接口请求是核心需求之一,但很多开发者会被「双进程环境」「跨域问题」「请求格式」等问题困扰。本文整理了一段真实的 Electron 接口请求开发对话,从基础疑问到实际落地,覆盖行业主流做法、代码实现、避坑要点,适合刚接触 Electron 或在接口请求中遇到问题的开发者参考,所有内容均为可直接复制、落地使用的实用干货。

一、核心疑问:Electron 接口请求到底该怎么写?

刚开始接触 Electron 开发,最容易困惑的就是:Electron 基于 Chromium 内核,为什么还要用 Node.js 处理请求?接口请求到底该写在渲染进程还是主进程?市面上别人都是怎么做的?

疑问1:Electron 不是 Chromium 开源内核运行的吗?怎么是 Node.js?

这是所有 Electron 开发者都会遇到的基础疑问,其实答案很简单:Electron 不是单一环境,而是「Chromium 渲染进程 + Node.js 主进程」的双内核架构,两者并行共存、完全隔离,各自承担不同职责:

  • 渲染进程(Renderer):基于完整 Chromium 浏览器内核,负责运行页面(HTML/CSS/JS、React/Vue 等),和普通 Chrome 浏览器完全一致,受浏览器安全限制(比如 CORS 跨域);
  • 主进程(Main):基于完整 Node.js 运行时,负责管理窗口、本地文件、系统 API、网络请求等,不受浏览器安全规则限制,没有跨域问题。

通俗理解:渲染进程是「内嵌的迷你 Chrome 浏览器」,主进程是「后台 Node 服务」,Electron 负责用 IPC(进程间通信)将两者打通,实现数据交互。

疑问2:接口请求必须写在主进程里吗?

答案:分场景,但 90% 的生产场景下,建议将接口请求写在主进程里,具体分两种主流方案(对应市面上不同项目规模的做法):

方案A:大厂/商用规范方案(推荐)

飞书、钉钉、VSCode 等商业级 Electron 应用均采用此方案,核心逻辑:

  • 渲染进程(页面):只触发请求、传递参数,不发起真实网络请求;
  • 主进程(Node.js):接收渲染进程的请求指令,发起真实 HTTP/HTTPS 请求,拿到结果后通过 IPC 返回给渲染进程;
  • 优势:彻底规避跨域、统一拦截 token、加密、日志,符合 Electron 安全规范,适配所有第三方接口和跨域场景。

方案B:中小型项目/快速开发方案(偷懒版)

个人开源、内部工具、外包项目常用此方案,核心逻辑:

  • 渲染进程直接用 axios/fetch 发起请求;
  • 关闭 Chromium 的跨域校验(webSecurity: false),无视浏览器 CORS 限制;
  • 优势:开发效率高,和普通前端项目写法一致,不用写 IPC、不用封装主进程;
  • 缺点:安全性低,不符合 Electron 安全规范,无法上架 Mac/Windows 应用商店,部分第三方接口可能出现隐性问题。

疑问3:webSecurity: false 真的能解决跨域吗?

是的!这行代码的核心作用是「直接关闭 Chromium 内核的同源策略(CORS 校验)」,相当于给内嵌的 Chrome 浏览器开启了「关闭跨域模式」,渲染进程可以直接请求任何域名、任何接口,无需主进程代理、无需前端代理。

但需注意:此配置仅适合开发环境、内部工具、不上架应用商店的项目,生产环境(商用、上架)必须关闭此配置,改用主进程请求方案。

疑问4:开发环境可以不走主进程吗?

完全可以!这是 Electron 开发的标准模式:开发环境和生产环境分开配置,兼顾效率和安全:

  • 开发环境:渲染进程直接发请求,关闭 webSecurity,和普通前端项目一样联调,不用写 IPC、preload,开发效率拉满;
  • 生产环境:切换到主进程请求,开启 webSecurity,使用自定义协议(如 app://),遵循安全规范。

疑问5:开发环境遇到 Connection refused 错误怎么办?

很多开发者会遇到这样的错误:finishConnect(..) failed: Connection refused: path-plan/192.168.50.164:18020,这里要明确:此错误和 Electron 无关、和跨域无关,纯纯是「网络连接失败」。

解决步骤(按顺序试,99% 能解决):

  1. 确认后端服务已启动,且监听的是 0.0.0.0:18020(不是 127.0.0.1,否则局域网无法访问);
  2. 关闭后端所在电脑的防火墙,或允许目标端口(如 18020)通过;
  3. 用浏览器直接访问 http://192.168.50.164:18020,验证是否能正常访问;
  4. 若浏览器也无法访问,说明是后端或网络问题,与 Electron 代码无关。

疑问6:生产环境有必要用 app:// 自定义协议吗?

非常有必要!开发环境我们可以用 localhost 访问,但生产环境如果用 file:// 路径,会遇到很多坑:

  • 跨平台路径混乱(Windows 和 macOS/Linux 路径格式不同);
  • Chromium 安全策略拦截资源(字体、图片、CSS 可能报 404);
  • 权限过宽,可能导致渲染进程意外访问系统文件;
  • 无法上架应用商店,审核会视为安全风险。

app:// 自定义协议可以解决以上所有问题,实现全平台路径统一、资源正常加载、权限可控,是生产环境的标准配置。

疑问7:表单请求(URLSearchParams 格式)怎么传递到主进程?

这是实际开发中最常用的场景(如登录、注册),核心坑点:渲染进程直接传递 URLSearchParams 实例会出现 IPC 序列化异常,正确做法是「渲染进程传普通对象,主进程再转表单格式」。

二、全流程落地代码(可直接复制使用)

结合以上所有疑问,整理一套完整的 Electron 接口请求落地代码,涵盖开发/生产环境适配、表单请求处理、自定义协议配置,直接复制到项目中即可使用。

1. 主进程(main.js):核心请求处理 + 环境适配

const { app, BrowserWindow, ipcMain, protocol } = require('electron');
const axios = require('axios');
const path = require('path');
const { URL } = require('url');

// 1. 注册 app:// 自定义协议(生产环境用)
protocol.registerSchemesAsPrivileged([
  {
    scheme: 'app',
    privileges: {
      standard: true,
      secure: true,
      allowServiceWorkers: true,
      supportFetchAPI: true,
      corsEnabled: true
    }
  }
]);

// 2. 主进程统一请求处理(支持表单请求和 JSON 请求)
ipcMain.handle('api:request', async (_, options) => {
  const { method, url, data, isForm = false } = options;
  const baseURL = 'http://floor.primerobotics.nepa'; // 你的后端基地址

  try {
    // 处理 URL:去掉 /api 前缀(根据业务需求调整)
    const finalURL = `${baseURL}${url.replace('/api/', '/')}`;

    // 构建请求配置
    const requestConfig = {
      method: method.toLowerCase(),
      url: finalURL,
      headers: {}
    };

    // 表单请求:主进程构建 URLSearchParams
    if (isForm) {
      const formData = new URLSearchParams(data);
      requestConfig.data = formData;
      requestConfig.headers['Content-Type'] = 'application/x-www-form-urlencoded';
    } else {
      // JSON 请求
      requestConfig.data = data;
      requestConfig.headers['Content-Type'] = 'application/json';
    }

    // 发起请求(Node 环境无跨域限制)
    const response = await axios(requestConfig);
    return {
      code: response.status,
      data: response.data,
      message: '请求成功'
    };
  } catch (error) {
    console.error('主进程请求失败:', error);
    return {
      code: error.response?.status || 500,
      data: null,
      message: error.message || '网络异常'
    };
  }
});

// 3. 创建窗口(适配开发/生产环境)
function createWindow() {
  const win = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true, // 安全隔离(必须开启)
      nodeIntegration: false, // 禁止渲染进程使用 Node.js(必须关闭)
      webSecurity: !!app.isPackaged // 开发环境关闭跨域,生产环境开启
    }
  });

  // 加载页面
  if (app.isPackaged) {
    // 生产环境:加载自定义协议页面
    protocol.registerFileProtocol('app', (request, callback) => {
      const url = new URL(request.url);
      const pathname = decodeURIComponent(url.pathname);
      const filePath = path.join(__dirname, 'dist', pathname);
      callback({ path: filePath });
    });
    win.loadURL('app://./index.html');
  } else {
    // 开发环境:加载本地 dev 服务(如 Vite 5173 端口)
    win.loadURL('http://localhost:5173');
    win.webContents.openDevTools(); // 打开开发者工具
  }
}

// 启动应用
app.whenReady().then(createWindow);

// 跨平台窗口关闭逻辑
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

2. 预加载脚本(preload.js):安全暴露 IPC 接口

const { contextBridge, ipcRenderer } = require('electron');

// 安全暴露 IPC 接口给渲染进程,避免直接暴露 Node.js 能力
contextBridge.exposeInMainWorld('electronAPI', {
  request: (options) => ipcRenderer.invoke('api:request', options)
});

3. 渲染进程 API 封装(userApi.js)

// 渲染进程 API 封装,统一调用主进程 IPC
export const userApi = {
  // 登录接口(表单请求,标记 isForm: true)
  login: (data) => {
    return window.electronAPI.request({
      method: 'POST',
      url: '/api/login',
      data: data, // 普通对象:{ username, password }
      isForm: true
    });
  },

  // 示例:普通 JSON 请求
  getUserInfo: (token) => {
    return window.electronAPI.request({
      method: 'GET',
      url: '/api/user/info',
      data: { token },
      isForm: false
    });
  }
};

4. 渲染进程页面调用(以登录为例)

// 登录页面(Vue 示例,React 写法类似)
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { userApi } from '@/api/userApi';
import { useUserStore } from '@/store/userStore';

const router = useRouter();
const userStore = useUserStore();
const loginForm = ref({
  username: '',
  password: ''
});

const handleLogin = async () => {
  try {
    // 直接传递普通对象,无需创建 URLSearchParams
    const res = await userApi.login(loginForm.value);
    
    if (res.code === 200) {
      ElMessage.success('登录成功');
      userStore.setToken(res.data.token);
      userStore.setUsername(loginForm.value.username);
      router.push('/');
    } else {
      ElMessage.error(res.message || '登录失败');
    }
  } catch (err) {
    ElMessage.error('网络异常,请稍后重试');
  }
};
</script>

5. URL 拼接工具(buildFullUrl.js)

针对接口路径拼接需求(如去掉 /api 前缀),封装通用工具函数:

// URL 拼接工具,自动处理 /api 前缀和完整地址
function buildFullUrl(url) {
  // 已是完整 HTTP/HTTPS 地址,直接返回
  if (url.startsWith('http://') || url.startsWith('https://')) {
    return url;
  }

  // 处理 /api/xxx 路径:去掉 /api 前缀
  if (url.startsWith('/api/')) {
    return `http://floor.primerobotics.nepa${url.replace('/api/', '/')}`;
  }

  // 处理 /robot/xxx 路径:正常拼接
  if (url.startsWith('/robot/')) {
    return `http://floor.primerobotics.nepa${url}`;
  }

  // 其他路径默认拼接基地址
  return `http://floor.primerobotics.nepa${url}`;
}

三、关键避坑要点(必看)

  1. 跨域问题:跨域是浏览器(渲染进程)的安全规则,Node.js(主进程)无跨域限制,生产环境优先用主进程代理请求,避免依赖 webSecurity: false;
  2. URLSearchParams 传递:渲染进程不要直接传递 URLSearchParams 实例,传普通对象,主进程再转表单格式,避免 IPC 序列化异常;
  3. 开发/生产环境区分:开发环境可关闭 webSecurity 简化配置,生产环境必须开启,同时使用 app:// 自定义协议,避免 file:// 路径坑;
  4. Connection refused 错误:优先检查后端是否启动、监听地址是否正确、防火墙是否关闭,与 Electron 代码无关;
  5. 安全规范:必须开启 contextIsolation: true、关闭 nodeIntegration: false,通过 preload.js 安全暴露 IPC 接口,避免安全漏洞;
  6. URL 拼接:如果后端接口不需要 /api 前缀,需在主进程或工具函数中处理,避免拼接出错误地址(如 http://xxx/api/path-plan 导致请求失败)。

四、行业现状与总结

结合市面上的实际开发情况,总结 Electron 接口请求的核心逻辑和选型建议:

  • 商用/长期项目/需上架应用商店:采用「主进程请求 + app:// 自定义协议 + 安全规范配置」,彻底规避跨域和安全风险;
  • 个人/内部工具/快速开发:采用「渲染进程直接请求 + webSecurity: false」,简化开发流程,提高效率;
  • 核心原则:渲染进程负责交互和参数传递,主进程负责真实请求和系统能力,这是 Electron 官方推荐、大厂通用的开发模式。

本文所有代码均来自真实开发对话,已验证可直接落地,覆盖了从基础疑问到实际代码的全流程,解决了 Electron 接口请求中最常见的跨域、表单处理、环境适配等问题。如果你的项目有特殊需求(如 token 统一携带、请求超时重试),可基于本文代码进一步扩展,满足企业级应用的需求。