挂钩所有 Fetch Api AJAX 请求

Posted

技术标签:

【中文标题】挂钩所有 Fetch Api AJAX 请求【英文标题】:Hook all Fetch Api AJAX requests 【发布时间】:2017-11-27 11:08:15 【问题描述】:

如何挂钩所有使用 Fetch Api 的 AJAX 请求?以前我们可以这样做来挂钩所有 XMLHttpRequest:

(function() 
    var origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() 
      console.log('request started!');
      this.addEventListener('load', function() 
          console.log('request completed!');
          console.log(this.readyState); //will always be 4 (ajax is completed successfully)
          console.log(this.responseText); //whatever the response was
      );
      origOpen.apply(this, arguments);
    ;
  )();

或者更好的是,如果你想添加到上面的函数中,你将如何挂钩所有 Fetch Api 和所有 XMLHttpRequest AJAX 请求?

【问题讨论】:

这个运气好吗?规范中似乎没有任何内容。 【参考方案1】:

由于 Promise 的性质,这很容易做到:

var taperFunction = response => response;
var originalFetch = fetch;
fetch = (input, init) => originalFetch(input, init)
    .then(response => new Promise((resolve) => 
        resolve(taperFunction(response));
    ));

还有一个更复杂(显式)的例子:

var originalFetch = fetch;
fetch = (input, init) => 
    debugger; // do what ever you want with request or reject it immediately
    return originalFetch(input, init).then(response => 
        // it is not important to create new Promise in ctor, we can await existing one, then wrap result into new one
        return new Promise((resolve) => 
            response.clone() // we can invoke `json()` only once, but clone do the trick
                .json()
                .then(json => 
                    debugger; // do what ever you want with response, even `resolve(new Response(body, options));`
                    resolve(response);
                );
        );
    );
;

【讨论】:

【参考方案2】:

其实FetchApi是原生浏览器支持的,只有一个接口:fetch。 构造函数返回一个Promise,当你想返回你的Promise重写fetch的构造函数时,你无法得到RequestResponse

下面的代码不好用。

(function() 
    var oldFectch = fetch;
    fetch.consotructor = function() 
        return new Promise(resolve, reject) 
            // your hook code
        ;
    ;
)();

那么,这是否意味着我们不能挂钩所有 Fetch Api ? 不!

首先,感谢window.fetch polyfill。

那么,让我们做点什么(编辑fetch.js)然后摇滚吧。

(function(self) 
    'use strict';

    // comment or delete the following code block
    // if (self.fetch) 
    //    return
    // 

    var support = 
        searchParams: 'URLSearchParams' in self,
        iterable: 'Symbol' in self && 'iterator' in Symbol,
        // ...

最后,把每件事都做得更好!

self.fetch = function(input, init) 
  return new Promise(function(resolve, reject) 
    var request = new Request(input, init)
    var xhr = new XMLHttpRequest()

    // Here we go!
    // Now we get the XMLHttpRequest Object
    // Do what you want!

    xhr.onload = function() 
      var options = 
        status: xhr.status,
        statusText: xhr.statusText,
        headers: parseHeaders(xhr.getAllResponseHeaders() || '')
      
      options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
      var body = 'response' in xhr ? xhr.response : xhr.responseText
      resolve(new Response(body, options))
    
    // ...

【讨论】:

【参考方案3】:

使用asyncawait,很简单:

const oldFetch = window.fetch
window.fetch = async (input, options) => 
    return await oldFetch(input, options)

一个例子:

const oldFetch = window.fetch
window.fetch = async (input, options) => 
    // modify the request here
    console.log(input)

    let response = await oldFetch(input, options)

    // modify the response
    let content = await response.text()
    content = 'goodbye!'
    return new Response(content, 
        status: response.status,
        statusText: response.statusText,
        headers: response.headers
    )


console.log(await (await fetch('/hello')).text())

【讨论】:

以上是关于挂钩所有 Fetch Api AJAX 请求的主要内容,如果未能解决你的问题,请参考以下文章

Ajax 发送OPTION请求

js fetch处理异步请求

ASP.NET MVC 5 - jQuery AJAX 有效,Fetch API 无效

javascript ajax 调用通过仅使用 POST 方法的 fetch 调用:奇怪的行为是 post 后跟 get plus 再次请求所有资源?

fetch.js是啥

使用 fetch API 时在 AJAX 调用期间显示微调器