【问题标题】:How do you handle CORS in an electron app?您如何在电子应用程序中处理 CORS?
【发布时间】:2018-12-17 16:09:20
【问题描述】:

我正在构建一个电子应用程序,需要在 API 提供者未启用 CORS 的情况下调用 API。通常建议的解决方案是使用反向代理,当使用 node 和 cors-anywhere 像这样在本地运行时,这很简单:

let port = (process.argv.length > 2) ? parseInt (process.argv[2]) : 8080; 
require ('cors-anywhere').createServer ().listen (port, 'localhost');

然后可以将应用配置为通过 localhost:8080 上的反向代理代理所有请求。

所以,我的问题是:

  1. 是否可以在电子应用程序中使用 node 和 cors-anywhere 来创建反向代理?我不想强制应用调用远程服务器。

  2. 在 Electron 应用程序中是否有更好或标准的方法来执行此操作?我假设我不是第一个遇到 CORS 问题的人。 :)

【问题讨论】:

  • 如果您在主进程(即不是渲染器进程)中发出 Web 请求,则无需担心 CORS。您如何以及在何处提出此类请求?
  • 我还没有将我的网络应用程序整合到 Electron 中。它目前只是一个 Ember.js 应用程序,必须使用反向代理,因为它正在浏览器中使用。
  • electron 中的“浏览器”可以设置为不安全模式以忽略 CORS。见stackoverflow.com/questions/30101537/…
  • 如果您在主进程中发出 Web 请求,nodejs 不尊重用户代理设置或支持 Windows 证书存储中的证书,因此您的应用程序将无法在某些人的机器上运行。通过主进程请求不是答案!

标签: node.js proxy cors electron reverse-proxy


【解决方案1】:

虽然我已经为现有答案苦苦挣扎了一段时间,但我将在这里提供最终对我有用的解决方案,假设您处于 main 进程中。

以下是所涉及的步骤:

  • 您需要访问session 对象,该对象可以通过以下两种方式之一获得:

    A) 通过全局session.defaultSession 在应用准备就绪后可用。

     const { session } = require('electron');
     const curSession = session.defaultSession;
    

    B) 另一种方法是通过BrowserWindow 上的会话,这假设windnows 已经创建。

     win = new BrowserWindow({});
     const curSession = win.webContents.session;
    

  • 一旦您拥有会话对象,您就可以将响应标头设置为您发送请求的站点。

    例如,假设您的电子浏览器窗口是从 http://localhost:3000 加载的,并且您正在向 example.com 发出请求,下面是一些示例代码:

     const { app, BrowserWindow, session } = require('electron');
     app.whenReady().then(_ => {
          // If using method B for the session you should first construct the BrowserWindow
          const filter = { urls: ['*://*.example.com/*'] };
    
          session.defaultSession.webRequest.onHeadersReceived(filter, (details, callback) => {
    
              details.responseHeaders['Access-Control-Allow-Origin'] = [ 'http://localhost:3000' ];
              callback({ responseHeaders: details.responseHeaders });
          }
          // Construct the BrowserWindow if haven't done so yet...
      }); 
    

【讨论】:

    【解决方案2】:

    在使用webRequest.onBeforeSendHeaders发送请求之前覆盖标头

    const filter = {
      urls: ['*://*.google.com/*']
    };
    const session = electron.remote.session
    session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
        details.requestHeaders['Origin'] = null;
        details.headers['Origin'] = null;
        callback({ requestHeaders: details.requestHeaders })
    });

    将这些代码放入渲染器进程中

    【讨论】:

      【解决方案3】:

      如果您在 localhost 中运行 Web 应用程序,请尝试此操作

      const filter = {
        urls: ['http://example.com/*'] // Remote API URS for which you are getting CORS error
      }
      
      browserWindow.webContents.session.webRequest.onBeforeSendHeaders(
        filter,
        (details, callback) => {
          details.requestHeaders.Origin = `http://example.com/*`
          callback({ requestHeaders: details.requestHeaders })
        }
      )
      
      browserWindow.webContents.session.webRequest.onHeadersReceived(
        filter,
        (details, callback) => {
          details.responseHeaders['access-control-allow-origin'] = [
            'capacitor-electron://-',
            'http://localhost:3000' // URL your local electron app hosted
          ]
          callback({ responseHeaders: details.responseHeaders })
        }
      )
      

      【讨论】:

      • Chrome 告诉我这个:The 'Access-Control-Allow-Origin' header contains multiple values 'capacitor-electron://-, http://localhost:3000', but only one is allowed.
      【解决方案4】:

      您可以让主进程,即运行 Electron 的 NodeJS 服务器发送请求。这避免了 CORS,因为这是一个服务器到服务器的请求。您可以使用IPC 将事件从前端(渲染进程)发送到主进程。在主进程中你可以listen这个事件,发送HTTP请求,返回一个promise给前端。

      在 main.js(创建 Electron 窗口的脚本)中:

      import { app, protocol, BrowserWindow, ipcMain } from ‘electron’
      import axios from 'axios'
      
      ipcMain.handle('auth', async (event, ...args) => {
        console.log('main: auth', event, args)  const result = await axios.post(
          'https://api.com/auth',
          {
            username: args[0].username,
            password: args[0].password,
            auth_type: args[1],
          },
        )  console.log('main: auth result', result)
        console.log('main: auth result.data', result.data)  return result.data
      })
      

      在你的前端 JS 中:

      import { ipcRenderer } from 'electron'
      
      sendAuthRequestUsingIpc() {
        return ipcRenderer.invoke('auth',
          {
            username: AuthService.username,
            password: AuthService.password,
          },
          'password',
        ).then((data) => {
          AuthService.AUTH_TOKEN = data['access_token']
          return true
        }).catch((resp) => console.warn(resp))
      }
      

      我写了一篇更深入的文章here

      【讨论】:

      • 您是否在处理 axios 请求时遇到延迟?我发现在主进程中快速检测到请求并调用 axios 函数,但 axios 调用需要几秒钟才能完成。如果我从渲染器进程中调用(禁用网络安全),那么 axios 调用响应几乎是即时的。
      【解决方案5】:

      在我的应用程序中,仅删除请求中的 Origin 标头(通过将其设置为 null)是不够的。我传递请求的服务器始终在响应中提供Access-Control-Allow-Origin 标头,不管Origin 标头是否存在于请求中。所以 Chrome 的嵌入式实例不喜欢 ACAO 标头与其对来源的理解不匹配。

      相反,我必须更改请求上的 Origin 标头,然后在响应的 Access-Control-Allow-Origin 标头上恢复它。

      app.on('ready', () => {
        // Modify the origin for all requests to the following urls.
        const filter = {
          urls: ['http://example.com/*']
        };
      
        session.defaultSession.webRequest.onBeforeSendHeaders(
          filter,
          (details, callback) => {
            console.log(details);
            details.requestHeaders['Origin'] = 'http://example.com';
            callback({ requestHeaders: details.requestHeaders });
          }
        );
      
        session.defaultSession.webRequest.onHeadersReceived(
          filter,
          (details, callback) => {
            console.log(details);
            details.responseHeaders['Access-Control-Allow-Origin'] = [
              'capacitor-electron://-'
            ];
            callback({ responseHeaders: details.responseHeaders });
          }
        );
      
        myCapacitorApp.init();
      });
      

      【讨论】:

      • 更改响应标头有效。
      【解决方案6】:

      今天刚遇到这个问题API calls with axios inside a React app bundled in Electron is returning 400

      据我所知,Electron 调用充当对 API url 的正常调用,这意味着它们不受 CORS 的影响。

      现在,当您使用 CORS 代理封装调用并对代理进行常规调用时,它应该会出现 400 错误,因为它不是 CORS 调用。 该线程解释了为什么 cors-anywhere 会这样响应 => https://github.com/Rob--W/cors-anywhere/issues/39

      我实际上是在 Electron 构建之前从应用程序中删除了我的 CORS 代理。由于我正在浏览器中进行测试,因此我仍然需要 CORS 代理进行开发。

      希望这会有所帮助。

      【讨论】:

        【解决方案7】:

        您是否尝试过使用fetch() 在此处查看如何使用 fetch 发出 no-cors 请求 https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en

        【讨论】:

        • 您使用什么 api 来拨打电话与规避 CORS 没有任何关系。这完全取决于客户。在这种情况下,浏览器默认强制执行 CORS,而通过 curl 或 node 进行的直接调用则不会。您提出的链接甚至说:“no-cors 旨在向没有 CORS 标头的其他来源发出请求并导致不透明的响应,但如前所述,目前在窗口全局范围内这是不可能的。”
        猜你喜欢
        • 2018-07-15
        • 1970-01-01
        • 2015-10-25
        • 2022-12-20
        • 2019-11-25
        • 2010-11-01
        • 2010-10-31
        • 2011-03-23
        相关资源
        最近更新 更多