背景——在开发Electron的过程中,我遇到了一个不太了解的问题:一个HTTP请求返回了403状态码,详细查看response信息显示是Cross-Origin Resource Sharing (CORS) Filter在作怪,提示“CORS origin denied:xxx”。即使我尝试在工程中设置webSecurity: false,问题依旧存在。
原因分析
Electron架构中包含两种主要进程:主进程(NodeJS进程)和渲染进程(Chromium进程)。渲染进程发起的网络请求会受到浏览器的CORS(跨源资源共享)限制,就像被设置了一道看不见的屏障。通过Postman和Developer Tools的深入分析,我发现:
-
对于简单请求(HEAD、GET、POST):会在请求头中添加0rigin"字段,成功响应的网络请求通常会在响应头中包含 ACCess-ontrol-ALlow-0rigin 字段,用于指定允许跨域访问的源:
a.Access-Control-Allow-Origin(必需):这是允许跨域的关键字段。
b.Access-Control-Allow-Credentials(可选):决定是否允许发送Cookie。
-
去除“Origin”字段:我发现,如果手动删除请求头中的“Origin”字段,请求就能成功访问目标服务。同样,直接在浏览器中打开该URL也不会包含“Origin”字段。
解决策略
-
渲染进程受限?交给主进程处理!
既然渲染进程受到限制,我们可以将网络请求转移到主进程中处理。利用IPC(进程间通信)机制,主进程和渲染进程可以高效沟通。在主进程中使用 ipcMain, 在渲染进程中使用 ipcRenderer,可以实现事件的发送和监听。
a.渲染进程的操作:
当渲染进程需要获取响应时,会调用 ipcRederer.invoke() 方法,发送一个get-response事件到主进程,并等待响应。
import { ipcRenderer } from 'electron'; //…… try{ const url = 'xxx'; let json: any = (); json = await ipcRenderer.invoke('get-response',url, host); //…… } catch (error){ //…… }
b.主进程的操作:
在主进程中,使用 ipcMain.handle() 方法来处理请求,并返回一个Promise。
import { ipcMain } from 'electron'; const https = require('https'); //…… app.on('ready',()=> { //…… ipcMain.handle('get-response', async (event,url)=> { try { const data= await new Promise((resolve, reject) => { //…… }); return data; } catch (error) { //…… } }); });
PS:主进程的网络请求不会在开发者工具当中看到。
-
去除“Origin”字段
如果"Origin"字段是罪魁祸首,那我们可以使用 webRequest API 在请求发送前悄悄修改它。
import { session } from 'electron'; //…… session.defaultsession.webRequest.onBeforeSendHeaders((details, callback) => { if (details.url.startsWith('xxx')) { delete details.requestHeaders['Origin']; // 或者将Origin设置为允许的值 // details.requestHeaders['Origin'] = 'https://allowed-origin.com'; } callback({ requestHeaders: details.requestHeaders }); }); //……
PS: session需要在主进程中使用,在渲染进程中,session是undefined。在我的场景中,请求头中多了“Origin”字段,也可以将“Origin”设置为域名白名单中的内容。
-
找服务端解决
(虽然这个方法听起来最简单,但我跟服务端的同学不太熟😂😂
不管怎样,问题总算是解决啦,每天都会遇到不懂的问题,害(流泪猫猫头o(TヘTo)