您如何在电子应用程序中处理 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?的主要内容,如果未能解决你的问题,请参考以下文章