详解electron如何拦截网络请求并处理响应
作者:小气小憩
需求背景
在electron
中的web应用离线方案,由于目前的web应用已经大量接入了,想要各个产线去对离线方案进行大量配置更改,成本过高。所以期望离线方案能对现有web应用进行无侵入接入。
实现方案
electron版本:12.1.1
方案一 onBeforeRequest + 自定义协议
使用webRequest.onBeforeRequest
拦截所有请求,通过判断逻辑指定处理某些url。
这些特定的url,需要通过自定义协议处理。
注册私有协议
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进行识别,转发到私有协议中
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等)的权限
protocol.registerSchemesAsPrivileged([ { scheme: 'toLocal', privileges: { standard: true, secure: true, supportFetchAPI: true, bypassCSP: true, corsEnabled: true, }, }, ]);
问题
使用onBeforeRequest
+redirectURL
转发的url,会直接改变web中的request url
。
私有协议无法使用cookie
在部分验证url的场景,会出现无权限/跨域问题
方案二 只使用拦截器
拦截自定义协议toLocal
和https
协议的请求,在https协议中处理指定url。命中后转发到toLocal
协议中,进行本地文件的读取。未命中则转发请求。
ses.protocol.interceptFileProtocol('toLocal', (request, callback) => { callback({ path: '/Users/xxx/Documents/project/electron-react/dist/xxx.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, }); } });
问题
拦截器只能注册一种,已先注册的为主。后续注册相同的scheme都会失败。
由于以上问题,拦截请求时,考虑使用interceptHttpProtocol
。
不代理的请求需要转发问题,request
中的uploadData
数据格式和response
中的uploadData
数据格式不一致。
当前代码中,处理post请求中,携带file时无法处理。目前正在解决中...
interceptHttpProtocol中的request和callback定义
// 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
ses.setProxy()
总结
以上方案目前都不太满足,调研了近3天,依旧没有一个很好的解决方案。
在开始调研前的方案
我是真没想到!我查了这么久,愣是没找到能对webContent的网络请求完全拦截的api!!!
方案一是目前最理想的方案了,开发成本极低。但是拦截html会出现问题,redirectURL导致web中的请求地址更改解决不了!
到此这篇关于详解electron如何拦截网络请求并处理响应的文章就介绍到这了,更多相关electron拦截网络请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!