在 JavaScript 中拦截 fetch() API 请求和响应

IT技术 javascript ajax xmlhttprequest interceptor fetch-api
2021-03-06 05:57:50

我想在 JavaScript 中拦截 fetch API 请求和响应。

例如,在发送请求之前,我想拦截请求 URL。一旦响应到达,我也想拦截它。

下面的代码用于拦截所有XMLHTTPRequests的响应

(function(open) {
  XMLHttpRequest.prototype.open = function(XMLHttpRequest) {
    var self = this;
    this.addEventListener("readystatechange", function() {
      if (this.responseText.length > 0 && 
          this.readyState == 4 && 
          this.responseURL.indexOf('www.google.com') >= 0) {

        Object.defineProperty(self, 'response', {
          get: function() { return bValue; },
          set: function(newValue) { bValue = newValue; },
          enumerable: true,
          configurable: true
        });
        self.response = 'updated value' // Intercepted Value 
      }
    }, false);
    open.apply(this, arguments);
  };
})(XMLHttpRequest.prototype.open);

我想为fetch()API实现相同的功能我怎样才能做到这一点?

5个回答

现有答案显示了fetch浏览器中模拟的一般结构,但省略了重要的细节。

接受的答案显示了更换的一般模式window.fetch与自定义实现其截取电话和转发参数的功能fetch但是,显示的模式不会让拦截函数对响应执行任何操作(例如,读取状态或正文或注入模拟),因此仅对记录请求参数有用。这是一个非常狭窄的用例。

这个答案使用一个async函数来让拦截器awaitfetchPromise上工作,并且大概可以处理响应(模拟、阅读等),但是(在撰写本文时)有一个多余的闭包,并且没有显示如何读取响应正文非-破坏性地。它还包含导致堆栈溢出的变量别名错误。

这个答案是迄今为止最完整的,但在回调中有一些不相关的噪音,并且没有提到任何关于克隆响应以使主体能够被拦截器收集的内容。它没有说明如何返回模拟。

这是一个最小的、完整的示例,它纠正了这些问题,展示了如何处理参数日志记录,通过克隆响应和(可选)提供模拟响应,在不损害原始调用者情况下读取正文

const {fetch: origFetch} = window;
window.fetch = async (...args) => {
  console.log("fetch called with args:", args);
  const response = await origFetch(...args);
  
  /* work with the cloned response in a separate promise
     chain -- could use the same chain with `await`. */
  response
    .clone()
    .json()
    .then(body => console.log("intercepted response:", body))
    .catch(err => console.error(err))
  ;
    
  /* the original response can be resolved unmodified: */
  //return response;
  
  /* or mock the response: */
  return {
    ok: true,
    status: 200,
    json: async () => ({
      userId: 1,
      id: 1,
      title: "Mocked!!",
      completed: false
    })
  };
};

// test it out with a typical fetch call
fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then(response => response.json())
  .then(json => console.log("original caller received:", json))
  .catch(err => console.error(err))
;

这是稳定性的最佳答案
2021-05-09 05:57:50

为了拦截获取请求和参数,我们可以采用下面提到的方式。它解决了我的问题。

 const constantMock = window.fetch;
 window.fetch = function() {
     // Get the parameter in arguments
     // Intercept the parameter here 
    return constantMock.apply(this, arguments)
 }
是的。它会拦截所有的 fetch API。但是您可以根据您可以进行操作在函数内部进行条件检查。只需控制台记录参数和此对象,您就可以看到所有值
2021-04-21 05:57:50
如何使用此代码。我们需要在这段代码之后调用 constantMock 吗?
2021-04-27 05:57:50
是获取所有浏览器 API 吗?详细说明我的应用程序加载了 SSO(OKTA),想要更改一个 api 响应,这可能吗?
2021-05-07 05:57:50
只需以常规方式调用 fetch API。我们已经定义了我们自己的 fetch,所以调用将在我们的函数定义中被拦截。
2021-05-15 05:57:50

为了拦截响应正文,您需要创建一个新的Promise并将当前解析或拒绝转换为“then”代码。它为我解决了并保留了真实应用程序的内容。例如。react等等。

const constantMock = window.fetch;
 window.fetch = function() {
  console.log(arguments);

    return new Promise((resolve, reject) => {
        constantMock.apply(this, arguments)
            .then((response) => {
                if(response.url.indexOf("/me") > -1 && response.type != "cors"){
                    console.log(response);
                    // do something for specificconditions
                }
                resolve(response);
            })
            .catch((error) => {
                reject(response);
            })
    });
 }
只是想补充一个细节:如果你需要响应体“为特定条件做一些事情”,不要忘记克隆响应,否则,Promise的最终用户会得到“TypeError:Body has been used”。所以,像“response.clone().json()”或“response.clone().text()”一样获取正文。
2021-04-29 05:57:50
const fetch = window.fetch;
window.fetch = (...args) => (async(args) => {
    var result = await fetch(...args);
    console.log(result); // intercept response here
    return result;
})(args);
这会破坏 Chrome 83.0 中的堆栈。const origFetch = window.fetchawait origFetch(...args)解决问题。另外,我不确定为什么最外层函数存在。您可以直接使用fetch = async (...args) => ...并跳过 IIFE。
2021-05-15 05:57:50

继 Hariharan 的回答之后,以下是我在每次获取请求之前和之后更新 Redux 中微调器状态的方法

import store from './../store';

// Set up interceptor on all fetch API calls
// Increments redux spinner state when api is called
// Decrements redux spinner state again when it is returned
(function() {
    const originalFetch = window.fetch;
    window.fetch = function() {
        store.dispatch({type: 'show-spinner'})
        return originalFetch.apply(this, arguments)
            .then((res) => {
                store.dispatch({type: 'hide-spinner'})
                return res;
            })
    }
})();