您如何在电子应用程序中处理 CORS?

Posted

技术标签:

【中文标题】您如何在电子应用程序中处理 CORS?【英文标题】:How do you handle CORS in an electron app? 【发布时间】: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 上的反向代理代理所有请求。

所以,我的问题是:

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

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

【问题讨论】:

如果您在主进程(即不是渲染器进程)中发出 Web 请求,则无需担心 CORS。您如何以及在何处提出此类请求? 我还没有将我的网络应用程序整合到 Electron 中。它目前只是一个 Ember.js 应用程序,必须使用反向代理,因为它正在浏览器中使用。 electron 中的“浏览器”可以设置为不安全模式以忽略 CORS。见***.com/questions/30101537/… 如果您在主进程中发出 Web 请求,nodejs 不尊重用户代理设置或支持 Windows 证书存储中的证书,因此您的应用程序将无法在某些人的机器上运行。通过主进程请求不是答案! 【参考方案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 标头的其他来源发出请求并导致不透明的响应,但如前所述,目前在窗口全局范围内这是不可能的。”

以上是关于您如何在电子应用程序中处理 CORS?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Web 应用程序中接收和处理电子邮件

如何在 SiteMinder 受保护的环境中处理 CORS 预检请求?

如何在 Prod/Dev 环境中处理 CORS URL?

在 Sharepoint 2013 中启用 CORS

如何在无服务器中允许 CORS 用于自定义标头?

如何在 API Gateway 和 React 应用程序之间启用 AWS 中的 CORS 策略?