愚墨的博客
  • 首页
  • 前端技术
  • 面试
只争朝夕不负韶华
  1. 首页
  2. 前端技术
  3. 正文

如何做到请求监听-ajax篇

2023年05月12日 3847点热度 2人点赞 0条评论

上一篇文章介绍了如何监听fetch API 发出的请求。本篇介绍一下如何监听ajax的请求。
监听本质上也是复写。

其中我们主要是覆写 setRequestHeader 方法和send方法,open方法也需要格外注意一点。
因为open、setRequestHeader、send方法都在xhr的原型上,所以这里需要对原型链有一点基本的理解。

另外 我们也需要考虑一点,如果有别的工具对xhr进行了覆写怎么办? 我们再次覆写会不会覆盖他们的?所以我们要考虑尽可能的不对其他业务造成影响。

实现上也很简单,就是覆写前先给存一份。其中要注意的细节和fetch都差不多。我们直接上代码

export function rewriteXHR() {

  if (window.location && window.location.protocol === 'file:') {
    return
  }

  try {
    // 为了防止其他复写的工具不受影响,这里先把这三个方法存一下
    const open = xhr.prototype.open
    const send = xhr.prototype.send
    const setRequestHeader =  xhr.prototype.setRequestHeader

    // 复写 open方法
    xhr.prototype.open = function(this, method: HttpMethod, url: string) {
      // 这里要注意,先执行一下已有的方法
      open && open.apply(this, arguments)
      this.__startTime = +new Date()
      this.method = method
      this.url = url
    }

    xhr.prototype.setRequestHeader = function(this, header: string, value: string) {
      // 这里要注意,先执行一下已有的方法
      setRequestHeader && setRequestHeader.apply(this, arguments)

      try {
        if (!this.headers) {
          this.headers = {}
        }

        if (!this.headers[header]) {
          this.headers[header] = []
        }

        if (this.headers[header].push) {
          this.headers[header].push(value)
        } else {
          this.headers[header] = [].concat(this.headers[header], value)
        }
      } catch {}
    }

    xhr.prototype.send = function(this, body) {
      // 这里要注意,先执行一下已有的方法
      send && send.apply(this, arguments)
      const ADD_EVENT_LISTENER = 'addEventListener'
      const ON_READY_STATE_CHANGE = 'onreadystatechange'
      const ReqEndDispatch = (event: Event) => {
        try {
          const ctx = event.currentTarget
          if (!event || !ctx) {
            return
          }

          const contentType = ctx.getResponseHeader('content-type') || ''
          if (contentType.toLowerCase().indexOf('application/json') < 0) {
            return
          }

          const response = (function() {
            try {
              return JSON.parse(ctx.response)
            } catch {
              return ctx.response
            }
          })()
          const duration = +new Date() - ctx.__startTime
          // 这是最终要上报的数据
         const finData =  {
            // 请求响应字段
            httpClient: 'XHR',
            duration,
            referrer: document.referrer,
            request: {
              url: ctx.url,
              method: ctx.method,
              params: getUrlParams(ctx.url), // 这些同fetch的复写
              data: getReqBodyData(body),  // 这些同fetch的复写
              headers: ctx.headers,
              timeout: ctx.timeout,
            },
            response: {
              data: response,
              headers: parseHeaders(ctx.getAllResponseHeaders()),  // 这些同fetch的复写
              status: ctx.status,
              statusText: ctx.statusText,
            },
          }

          // 这里做一下上报
          // 。。。。。。
        } catch (error) {
          console.log('[rewriteXHR.send] request end callback error:', error)
        }
      }
      // 这里做一下监听
      if (ADD_EVENT_LISTENER in this) {
        this[ADD_EVENT_LISTENER]('load', ReqEndDispatch)
        this[ADD_EVENT_LISTENER]('error', ReqEndDispatch)
        this[ADD_EVENT_LISTENER]('abort', ReqEndDispatch)
        this[ADD_EVENT_LISTENER]('timeout', ReqEndDispatch)
      } else {
        let userCallback: Function = this[ON_READY_STATE_CHANGE]
        // @ts-ignore
        this[ON_READY_STATE_CHANGE] = (event: Event) => {
          if (this.readyState === 4) {
            ReqEndDispatch(event)
          }
          userCallback && userCallback.apply(this, arguments)
        }
      }
    }
  } catch (error) {
    console.log('[rewriteXHR] rewrite error:', error)
  }
}

两篇文章我们介绍了如何监听请求以及实现方法,通过复写fetch和ajax来实现。
在项目中的实际使用 要将复写方法放到入口处前置执行。这样才能cover住全部的请求。另外,如果有一些白名单的域名不想被监听,在获取数据前应该叫一个过滤,这里就不实现了。

这算是前端监听请求最通俗简单的方法了。当然如果从功能实现上来讲,还是有比较多的方法来实现的。如果在服务器Nginx层去做,或者使用service work去做。

标签: 暂无
最后更新:2023年07月23日

愚墨

保持饥渴的专注,追求最佳的品质

点赞
< 上一篇

文章评论

取消回复

搜搜看看
历史遗迹
  • 2023年5月
  • 2022年9月
  • 2022年3月
  • 2022年2月
  • 2021年12月
  • 2021年8月
  • 2021年7月
  • 2021年5月
  • 2021年4月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年9月
  • 2020年7月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年1月
  • 2019年5月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年9月
  • 2018年3月
  • 2018年2月
  • 2018年1月
  • 2017年11月
  • 2017年7月
  • 2017年6月
  • 2017年3月
  • 2017年2月
  • 2017年1月
  • 2016年12月
  • 2016年11月
  • 2016年9月
  • 2016年8月
  • 2016年7月
  • 2016年6月
  • 2016年5月
  • 2016年4月
  • 2016年3月
  • 2016年2月
  • 2016年1月
  • 2015年12月
  • 2015年10月
  • 2015年9月
  • 2015年7月
  • 2015年6月
  • 2015年4月

COPYRIGHT © 2020 愚墨的博客. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS