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!!!

蹲一个大佬救命

相关推荐
丁总学Java11 分钟前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
懒羊羊大王呀22 分钟前
CSS——属性值计算
前端·css
无咎.lsy1 小时前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec1 小时前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
YUELEI1181 小时前
构建electron项目
electron·vue3
fishmemory7sec1 小时前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆2 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
看到请催我学习2 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
twins35203 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky3 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js