1. 背景
前端开发的产物通常是 app.js
、app.css
,然后将这些资源放在真实环境域名下进行工作的。
但前端的开发环境通常是本地的 http://localhost:xxx
,业务域名可能是 https://xxx.abc.com
,两者不在一个域名下在调用接口或者调试时会非常不方便。
加上现在的构建或脚手架工具,如 Vite、Webpack5、Umi 等在开发环境下采用 ES Module 构建,线上采用单文件构建,导致传统的,访问业务域名,然后将资源代理到本地的方式出现问题,所以更合适的方式应该是如何在前端的本地环境下,可以流畅的访问业务域名下的接口。
期待:
2. 实现思路
实现这种方法主要解决:CORS 问题,及 Same-site Cookies 的问题(非登陆态场景不需要解决 Cookie 问题,但很少见此类场景)。
这两个问题,通常业务侧是不能给解决的,所以需要前端自行处理这两个问题。
3. 实现方法
下面罗列下实现期望中,在本地开发环境,顺畅调用业务接口的方法。
3.1 系统级代理实现
针对 CORS 及 Same-site 处理,浏览器级代理是完全无法处理的,当然从调用方向来看也是不合适的,所以需要系统级代理进行拦截重写。
这里推荐 Whistle 或者 Charless 实现。
如 Whistle 可以使用:
pattern resCors://* reqCookies://filepath
缺点:
- 系统级代理,全局受影响,影响网络速度及系统性能。
- session 的 cookie 需要定期更新,非常繁琐。
- 部分代理工具无法转发拦截本地请求。
3.2 前端工程侧代理
这个属于比较成熟的方案了,内部通过配置 proxy 可以实现应用及的代理转发。
3.2.1 Webpack OR Umi
Webpack 和 Umi 采用相似的代理方案,底层使用 http-proxy-middleware 实现,可以很方便修改请求和响应,实现 CORS 及 Same-site 的处理。
可以在 webpack.config.js
或者 Umi 的 config.js
中配置:
export default {
{
'/api/': {
target: "https://xxx.abc.com",
changeOrigin: true,
secure:false,
onProxyReq: (proxyReq) => {
proxyReq.setHeader(
'Cookie',
'sessionId=wzqa6tjqgl;sessionId2=wzqa6tjqgl',
)
},
},
}
}
3.2.2 Vite
Vite 底层通过 node-http-proxy 实现代理功能。
可以在 vite.config.js
进行配置:
export default {
server: {
proxy: {
"/api": {
target: "https://xxx.abc.com",
changeOrigin: true,
},
},
},
plugins: [
{
name: "append-backend-cookies",
configureServer(server) {
server.middlewares.use((req, res, next) => {
req.headers["Cookie"] = "sessionId=wzqa6tjqgl;sessionId2=wzqa6tjqgl";
next();
});
},
},
],
};
优点:
- 相比系统级,不会对全局系统代理产生影响,系统性能影响小。
- 不用额外的其他工具软件支持,简单方便。
缺点:
- session 的 cookie 需要定期更新,非常繁琐。
3.3 完美方案 Electron 修改 Webview
CORS 的问题本质是浏览器的限制,Same-site 导致的登陆 Cookies 无法携带也是浏览器的限制,那直接把浏览器的限制给去掉不就可以迎刃而解了。这样不需要 api 的域名转发了,也不需要代理了,直接在 localhost 下调用 xxx.abc.com 就可以了。且在 Electron 中也可以正常安装浏览器插件,像 React-Devtools 完全正常使用。
早期 Electron 版本只需要 webSecurity: false
就可以解决我们的问题,但在 Chrome 94 版本不再放行,无法再携带 Cookies,也就是 Electron v14 之后的版本无法通过 webSecurity: false
来禁用这些问题。
新版本(v27),通过复写 onHeadersReceived
实现:
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
webSecurity: false,
},
});
win.loadURL("https://localhost:3000");
// win.webContents.openDevTools();
}
function disableSamesiteCookies(filter = ["*://*/*"]) {
session.defaultSession.webRequest.onHeadersReceived(
{ urls: filter },
(details, callback) => {
const newCookies = [];
details?.responseHeaders?.["set-cookie"]?.map((item) =>
newCookies.push(item.split("; ")[0] + "; Secure; SameSite=None")
);
details.responseHeaders["set-cookie"] = newCookies;
callback({ cancel: false, responseHeaders: details.responseHeaders });
}
);
}
app.whenReady().then(() => {
createWindow();
disableSamesiteCookies();
// ……
});
优点:
- 几乎全是优点。
缺点:
- 需要额外开启 electron 项目。
4. 链接
- https://github.com/http-party/node-http-proxy#options
- https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure?hl=zh-cn
- https://www.chromium.org/updates/same-site/
- https://github.com/GoogleChromeLabs/samesite-examples
- https://releases.electronjs.org/releases/stable?version=14
- https://github.com/lecepin/Debugging-env-browser
- https://webpack.js.org/configuration/dev-server/#devserverproxy
- https://vitejs.dev/config/server-options.html#server-proxy
- https://github.com/chimurai/http-proxy-middleware#options
原文地址: Github