electron如何拦截网络请求并处理响应

目前方案不完善,期望大佬能发表一下建议~ 尝试了很多api,都不能很好的达到效果

需求背景

electron中的web应用离线方案,由于目前的web应用已经大量接入了,想要各个产线去对离线方案进行大量配置更改,成本过高。所以期望离线方案能对现有web应用进行无侵入接入。

实现方案

<math xmlns="http://www.w3.org/1998/Math/MathML"> e l e c t r o n 版本 : 12.1.1 \color{#a0a0a0}{electron 版本:12.1.1} </math>electron版本:12.1.1

方案一 onBeforeRequest + 自定义协议

使用webRequest.onBeforeRequest拦截所有请求,通过判断逻辑指定处理某些url。

这些特定的url,需要通过自定义协议处理。

  • 注册私有协议
ts 复制代码
const ses = session.fromPartition('persist:my-partition');
ses.protocol.registerFileProtocol('toLocal', (request, callback) => {
  //这只是个举例,实际需要通过request.url判断使用哪个本地文件
  callback({
    path: '/Users/xxx/Documents/project/electron-react/dist/xxx.html',
    statusCode: 200,
    charset: 'utf-8',
    mimeType: 'text/html',
  });
  • 对特定url进行识别,转发到私有协议中
ts 复制代码
ses.webRequest.onBeforeRequest({ urls: ['*://*/*'] }, (details: any, callback: any) => {
  // 检查请求URL是否为特定资源
  if (details.url === 'https://xx.xx.com/') {
    callback({
      redirectURL: 'toLocal://xx.xx.com',
    });
  } else {
    // 允许其他请求正常进行
    callback({});
  }
});
  • 自定义协议在拦截html时,需要开启访问session(localstorage,cookie等)的权限
php 复制代码
protocol.registerSchemesAsPrivileged([
  {
    scheme: 'toLocal',
    privileges: {
      standard: true,
      secure: true,
      supportFetchAPI: true,
      bypassCSP: true,
      corsEnabled: true,
    },
  },
]);

问题

  1. 使用onBeforeRequest+redirectURL转发的url,会直接改变web中的request url

    1. 私有协议无法使用cookie

    2. 在部分验证url的场景,会出现无权限问题

方案二 只使用拦截器

拦截自定义协议toLocalhttps协议的请求,在https协议中处理指定url。命中后转发到toLocal协议中,进行本地文件的读取。未命中则转发请求。

ts 复制代码
ses.protocol.interceptFileProtocol('toLocal', (request, callback) => {
    callback({
      path: '/Users/ext.sunhaosheng1/Documents/project/electron-react1/dist/aaa.html',
    });
  });
ses.protocol.interceptHttpProtocol('https', (request, callback) => {
    if (request.url === 'https://xxx.xx.com/') {
      callback({
        url: 'toLocal://xxx.xx.com',
        method: request.method,
      });
    } else {
      const uploadData = request.uploadData && {
        contentType: 'application/json',
        data: request.uploadData.map((i) => i.bytes.toString()).join(''),
      };
      callback({
        url: request.url,
        method: request.method,
        session: session.defaultSession,
        uploadData,
      });
    }
  });

问题

  1. 拦截器只能注册一种,已先注册的为主。后续注册相同的scheme都会失败。

  2. 由于以上问题,拦截请求时,考虑使用interceptHttpProtocol

    1. 不代理的请求需要转发问题,request中的uploadData数据格式和response中的uploadData数据格式不一致。
    2. 当前代码中,处理post请求中,携带file时无法处理。目前正在解决中...

interceptHttpProtocol中的request和callback定义

ts 复制代码
// request定义
interface ProtocolRequest {
    // Docs: https://electronjs.org/docs/api/structures/protocol-request
    headers: Record<string, string>;
    method: string;
    referrer: string;
    uploadData?: UploadData[];
    url: string;
  }
  
  interface UploadData {

    // Docs: https://electronjs.org/docs/api/structures/upload-data

    /**
     * UUID of blob data. Use ses.getBlobData method to retrieve the data.
     */
    blobUUID?: string;
    /**
     * Content being sent.
     */
    bytes: Buffer;
    /**
     * Path of file being uploaded.
     */
    file?: string;
  }
//  callback传参定义
 interface ProtocolResponse {

    // Docs: https://electronjs.org/docs/api/structures/protocol-response

    /**
     * The charset of response body, default is `"utf-8"`.
     */
    charset?: string;
    /**
     * The response body. When returning stream as response, this is a Node.js readable
     * stream representing the response body. When returning `Buffer` as response, this
     * is a `Buffer`. When returning `String` as response, this is a `String`. This is
     * ignored for other types of responses.
     */
    data?: (Buffer) | (string) | (NodeJS.ReadableStream);
    /**
     * When assigned, the `request` will fail with the `error` number . For the
     * available error numbers you can use, please see the net error list.
     */
    error?: number;
    /**
     * An object containing the response headers. The keys must be String, and values
     * must be either String or Array of String.
     */
    headers?: Record<string, (string) | (string[])>;
    /**
     * The HTTP `method`. This is only used for file and URL responses.
     */
    method?: string;
    /**
     * The MIME type of response body, default is `"text/html"`. Setting `mimeType`
     * would implicitly set the `content-type` header in response, but if
     * `content-type` is already set in `headers`, the `mimeType` would be ignored.
     */
    mimeType?: string;
    /**
     * Path to the file which would be sent as response body. This is only used for
     * file responses.
     */
    path?: string;
    /**
     * The `referrer` URL. This is only used for file and URL responses.
     */
    referrer?: string;
    /**
     * The session used for requesting URL, by default the HTTP request will reuse the
     * current session. Setting `session` to `null` would use a random independent
     * session. This is only used for URL responses.
     */
    session?: Session;
    /**
     * The HTTP response code, default is 200.
     */
    statusCode?: number;
    /**
     * The data used as upload data. This is only used for URL responses when `method`
     * is `"POST"`.
     */
    uploadData?: ProtocolResponseUploadData;
    /**
     * Download the `url` and pipe the result as response body. This is only used for
     * URL responses.
     */
    url?: string;
  }
  
 interface ProtocolResponseUploadData {
    // Docs: https://electronjs.org/docs/api/structures/protocol-response-upload-data
    /**
     * MIME type of the content.
     */
    contentType: string;
    /**
     * Content to be sent.
     */
    data: (string) | (Buffer);
  }

方案三:代理

安全过不去 弃

由于安全过不去,基本没做调研,只知道有个setProxy

ts 复制代码
ses.setProxy()

以上方案目前都不太满足,调研了近3天,依旧没有一个很好的解决方案。

在开始调研前的方案

graph TD renderer发起网络请求 --> main拦截 main拦截 --> 调用离线缓存sdk --> q1{命中缓存} q1 --是--> 返回本地文件 q1 --否--> 交还给electron原生请求

我是真没想到!我查了这么久,愣是没找到能对webContent的网络请求完全拦截的api!!!

蹲一个大佬救命

相关推荐
星空下的曙光几秒前
Node.js crypto模块所有 API 详解 + 常用 API + 使用场景
算法·node.js·哈希算法
李鸿耀3 分钟前
仅用几行 CSS,实现优雅的渐变边框效果
前端
码事漫谈22 分钟前
解决 Anki 启动器下载错误的完整指南
前端
im_AMBER42 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
蓝胖子的多啦A梦1 小时前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码1 小时前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.2 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
DoraBigHead3 小时前
React 架构重生记:从递归地狱到时间切片
前端·javascript·react.js