electron中使用axios解决cors问题

场景

在electron中集成axios后,发现axios不能读取到set-cookie响应头,且axios也无法写入Cookie响应头。

原因

Chromium中不支持跨站访问HttpOnly的Cookie,electron有两种进程一个是主进程,一个是渲染进程,它的渲染进程就是Chromium的进程,主进程是是NodeJS的进程。electron是使用Chromium的进程来渲染界面的,故axios不能修改和读取Chromium中Cookie,因为服务器端设置HttpOnly。

思路

利用electron的IPC消息机制,在渲染进程(Chromium进程)中触发网络请求事件,将该事件发送给electron的主进程(NodeJS进程),在主进程中进行axios网络请求调用远程服务器API,因为主进程是NodeJS环境,不是Chromium浏览器环境,不受CORS限制。最后,处理好远程服务器API数据后,将再次利用electron的IPC消息机制,将数据发回渲染进程进行UI界面渲染。

electron

这里假设已经完成了electron快速教程,即:
https://www.electronjs.org/docs/latest/tutorial/quick-start
并且,已经安装好axios库,即:

npm install axios

在Electro基础项目代码上面,主要做如下修改:

main.js

const {
  app,
  BrowserWindow,
  BrowserView,
  session,
  ipcMain,
  WebContents,
} = require("electron");
const axios = require("axios").default;
// include the Node.js 'path' module at the top of your file
const path = require("path");

const baseURL = "https://www.xxxx.com";
var myCookie;
const axiosClient = axios.create({
  baseURL,
  headers: {
    "Session-Access-Origin": "xxx",
    "Content-Type": "application/json",
  },
});
axiosClient.interceptors.request.use(
  (config) => {
    if (myCookie !== undefined) {
    // 添加Cookie请求头
      config.headers["Cookie"] = myCookie;
    }
    return config;
  },
  (error) => Promise.reject(error.request.data.err)
);

function createWindow() {
  const win = new BrowserWindow({
    width: 1920,
    height: 1080,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
      nodeIntegration: true,
      contextIsolation: false,
    },
  });

  win.webContents.openDevTools();
  win.loadFile("index.html");
}

app.whenReady().then(() => {
  createWindow();

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

app.on("window-all-closed", function () {
  if (process.platform !== "darwin") app.quit();
});

//ipcMain.on 将接受 从渲染进程发过来的“btnclick”事件
ipcMain.on("btnclick", function (event, arg) {
  var data = JSON.stringify({
    account: "xxx",
    password: "xxxx",
  });

  axiosClient
    .post("/api/user/userLogin", data)
    .then((res) => {
    // 登录成功后,获取set-cookie
      myCookie = res.headers["set-cookie"][0].split(";")[0];
      console.log(myCookie);
		// 发送login-task-finished事件
		// 将数据发生给渲染进程,进行刷新UI界面
      event.sender.send("login-task-finished", myCookie);
    })
    .catch((err) => console.log(err));
});

//ipcMain.on 将接受 从渲染进程发过来的“getTime”事件
ipcMain.on("getTime", function (event, arg) {
// 再次测试是否登录成功,这个接口只有登录成功才能够正常调用
  axiosClient
    .get("/api/common/getNow")
    .then((res) => {
      const data = res.data;
      console.log(data);
      // 发送getTime-task-finished事件
		// 将数据发生给渲染进程,进行刷新UI界面
      event.sender.send("getTime-task-finished", data.data);
    })
    .catch((err) => console.log(err));
});

renderer.js

// 引入electron的IPC消息模块
const ipcRenderer = require("electron").ipcRenderer;

const btnclick = document.getElementById("loadnewwindow");
btnclick.addEventListener("click", function () {
  var arg = "secondparam";

  //发送btnclick事件给主线程
  ipcRenderer.send("btnclick", arg); // ipcRender.send will pass the information to main process
});

const getTimeclick = document.getElementById("get_time");
getTimeclick.addEventListener("click", function () {
  var arg = "gettime";

  //发送getTime事件给主线程
  ipcRenderer.send("getTime", arg); // ipcRender.send will pass the information to main process
});
// 接收主线程的login-task-finished事件
ipcRenderer.on("login-task-finished", (event, param) => {
  var div = document.getElementById("session");
  div.innerHTML = param;
});
// 接收主线程的getTime-task-finished事件
ipcRenderer.on("getTime-task-finished", (event, param) => {
  var div = document.getElementById("time");
  div.innerHTML = param;
});

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta
      http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>, Chromium
    <span id="chrome-version"></span>, and Electron
    <span id="electron-version"></span>.
    <button id="loadnewwindow">login</button>
    <br />
    SESSION:
    <label id="session"></label>
    <br />
    <button id="get_time">get time</button>
    <br />
    Time:
    <label id="time"></label>
    <br />
<!-- 渲染进程脚本 -->
    <script src="./renderer.js"></script>
  </body>
</html>

peload.js

window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
  }
})

总结

electron主进程是NodeJS进程,不是Chromium,不受CORS限制;渲染进程是Chromium进程,受CORS限制。让axios在主进程中运行,就可以突破CORS,改读取和修改Cookie了。这里的主进程相当于electron的后台主进程,渲染先进就是用来渲染UI用的,某种程度上面在前端的前后分离。electron的主进程概念,与Android主线程作用不同。Android的主线程是不能干耗时的任务的,electron主进程是可以干耗时的操作,而不影响渲染进程。
还有,大能提出使用axios-cookiejar-support库,来增强axios,这里没有尝试过,就到此为止了。
最后,给出源代码参考。
https://github.com/fxtxz2/electron_axios_cors_cookie

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值