跨端实现之网络库拦截

工作中有些一些场景要拦截或改写网络库,比如在做跨端基建的时候,移动端同学说:原生拦截不好使,我和你约定一个request 方法吧。你直接用这个 request 方法调用。在你质疑他技术水平 :)的同时,我们可以思考一下这个场景。如果 H5 侧要拦截所有网络请求并用 request,大家怎么做。

业务同学使用的第三方网络库都不一样,axios,fetch,fly,一大堆。但大家观察一下 chrome 的请求类型基础就两个 fetch 和 XHR。那么搞定这两个我们的这个问题就解决了。

基本结构很简单,你让 llm 帮你写即可,我们仅聊聊思路和跨端场景下的边界场景。首先是劫持或是替换 fetch 和 XHR,以 fetch 举例。

js 复制代码
// ez code
const originalFetch = globalThis.fetch
const originalXMLHttpRequest = globalThis.XMLHttpRequest

globalThis.fetch = myFetch 
globalThis.XMLHttpRequest = myXMLHttpRequest

// my fetch

const myFetch = async function (
    input: RequestInfo | URL,
    options: RequestInit = {},
  ): Promise<Response> {
    try {
      // 处理请求参数
      const url = input.toString()
      const method = options?.method || 'GET'
      const headers = {}
      const body = xxx
  

      // customRequest 是移动端给的一个网络请求han'shu
      const response = await customRequest({
        body: JSON.stringify(body),
        url,
        method,
        headers,
        timeout: 10000,
      });

      try {
        response.data = JSON.parse(response.responseText);
      } catch {
        console.error(
          "Failed to parse response as JSON, using raw response text."
        );
        response.data = {};
      }
      const out = {
        ok: response.statusCode >= 200 && response.statusCode < 300,
        status: response.statusCode,
        statusText: `${response.statusCode}` || "",
        headers: new Headers(response.headers || {}),
        json: () => {
          return Promise.resolve(response.data);
        },
        text: () => Promise.resolve(response.responseText || ""),
        blob: () => Promise.reject(new Error("Blob not supported in fly")),
        formData: () =>
          Promise.reject(new Error("FormData not supported in fly")),
        arrayBuffer: () => Promise.resolve(response.data),
        clone: () => ({
          ok: response.statusCode >= 200 && response.statusCode < 300,
          status: response.statusCode,
          statusText: `${response.statusCode}` || "",
          headers: new Headers(response.headers || {}),
          json: () => Promise.resolve(response.data),
          text: () => Promise.resolve(response.responseText || ""),
          blob: () => Promise.reject(new Error("Blob not supported in fly")),
          formData: () =>
            Promise.reject(new Error("FormData not supported in fly")),
          arrayBuffer: () => Promise.resolve(response.data),
        }),
        data: {
          ...response.data,
          json: () => {
            return Promise.resolve(response.data);
          },
        },
        body: new ReadableStream({
          start(controller) {
            // 将响应数据推送到流中
            const data = response.data || response.responseText || "";
            const encoder = new TextEncoder();
            controller.enqueue(
              encoder.encode(
                typeof data === "string" ? data : JSON.stringify(data)
              )
            );
            controller.close();
          },
        }),
        redirected: false,
        type: "",
        bytes: null,
        bodyUsed: false,
        url,
      } as Response;
      return out
    } catch (error) {
      // 创建一个错误响应
    }
}

这一块需要注意 fetch 请求的 body 需要一个 ReadStream 类型。大多数第三方库会使用这个值。如果没有这个值,即使你请求正常也会拿到空对象(如果第三方库的默认是空)。

常见问题

这一块你基本上可以用 vibe coding。但是代码结构非常重要,因为会有一些边界场景,比如 override 代码(运行时代码执行比业务代码慢导致部分网络请求用原生 fetch / XHR)。有几个思路,

方案一:阻塞业务代码

阻塞主流程直到网络库覆盖完成。优点是直截了当,非常粗暴。但问题是

  1. 运行时代码侵入到业务代码里了
  2. 移动端提供的 request 方法可能不可用,因为 H5 和移动端往往通过 bridge 桥接。桥接流程如果存在同步操作。可能移动端在第一时间无法正常承接你的网络请求,但请求已经进去了。

方案二:网络库重发

相对和业务代码解耦,但问题是

  1. 业务代码注定要面临前几次网络请求被重发的问题。如果处理不好,用户可能会看到接口报错

方案三:容器侧注入运行时

相对常用的运行时注入方案,先对逻辑容器注入,运行时对象。

对于 iOS

swift 复制代码
jsContext.executeJavaScript(`globalfetch = xxx`)

等移动端确定后再加载 H5 的代码,建立业务实例。这样可以完全解耦。

相关推荐
阿杆11 分钟前
文心快码 3.5S 发布!实测插件开发,Architect 模式令人惊艳
前端·后端·文心快码
文心快码BaiduComate12 分钟前
我用Comate搭建「公园找搭子」神器,再也不孤单啦~
前端·后端·微信小程序
全栈技术负责人22 分钟前
前端全链路质量监控体系建设与实践分享
前端·系统架构·前端框架
sorryhc37 分钟前
0~1构建一个mini blot.new(无AI版本)
前端·前端框架·openai
南方者1 小时前
文心文心,其利锻心!这个古风射覆,它帅到我了!文心快码 3.5S
前端·敏捷开发·文心快码
永日456701 小时前
学习日记-CSS-day53-9.11
前端·css·学习
云枫晖1 小时前
JS核心知识-this的指向
前端·javascript
magnet1 小时前
用img标签渲染的svg VS 直接使用svg标签,有什么区别?
前端·html
ze_juejin1 小时前
createComponent的environmentInjector详解
前端