在 JavaScript 中拦截 fetch() API 请求和响应
Intercept fetch() API requests and responses in JavaScript
我想在 JavaScript 中拦截 fetch API 请求和响应。
比如在发送请求之前我想拦截请求URL。我也想在响应到达后拦截它。
以下代码用于拦截所有 XMLHTTPRequest
的响应。
(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 实现相同的功能。我该怎么做?
为了拦截获取请求和参数,我们可以按照下面提到的方式进行。它解决了我的问题。
const constantMock = window.fetch;
window.fetch = function() {
// Get the parameter in arguments
// Intercept the parameter here
return constantMock.apply(this, arguments)
}
为了拦截响应主体,您需要创建一个新的 Promise 并将当前解析或拒绝为 "then" 代码。它为我解决了并保留了真实应用程序的内容。例如。反应等..
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);
})
});
}
const fetch = window.fetch;
window.fetch = (...args) => (async(args) => {
var result = await fetch(...args);
console.log(result); // intercept response here
return result;
})(args);
继 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;
})
}
})();
现有答案显示了在浏览器中模拟 fetch
的一般结构,但省略了重要的细节。
显示了用自定义实现替换 window.fetch
函数的一般模式,该实现拦截调用并将参数转发给 fetch
。但是,显示的模式不允许拦截函数对响应做任何事情(例如,读取状态或正文或注入模拟),因此仅对记录请求参数有用。这是一个非常狭窄的用例。
使用 async
函数让 fetch
上的拦截器 await
promise 并可能处理响应(模拟、读取等)但是 (在撰写本文时)有一个多余的闭包,并且没有说明如何非破坏性地读取响应主体。它还包含一个导致堆栈溢出的变量别名错误。
是迄今为止最完整的,但在回调中有一些不相关的噪音,并且没有提及任何关于克隆响应以使主体能够被拦截器收集的内容。它没有说明如何返回模拟。
这是一个纠正这些问题的最小的完整示例,展示了如何处理参数日志记录、读取正文而不伤害原始调用者以及(可选)提供模拟响应。
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))
;
我想在 JavaScript 中拦截 fetch API 请求和响应。
比如在发送请求之前我想拦截请求URL。我也想在响应到达后拦截它。
以下代码用于拦截所有 XMLHTTPRequest
的响应。
(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 实现相同的功能。我该怎么做?
为了拦截获取请求和参数,我们可以按照下面提到的方式进行。它解决了我的问题。
const constantMock = window.fetch;
window.fetch = function() {
// Get the parameter in arguments
// Intercept the parameter here
return constantMock.apply(this, arguments)
}
为了拦截响应主体,您需要创建一个新的 Promise 并将当前解析或拒绝为 "then" 代码。它为我解决了并保留了真实应用程序的内容。例如。反应等..
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);
})
});
}
const fetch = window.fetch;
window.fetch = (...args) => (async(args) => {
var result = await fetch(...args);
console.log(result); // intercept response here
return result;
})(args);
继 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;
})
}
})();
现有答案显示了在浏览器中模拟 fetch
的一般结构,但省略了重要的细节。
window.fetch
函数的一般模式,该实现拦截调用并将参数转发给 fetch
。但是,显示的模式不允许拦截函数对响应做任何事情(例如,读取状态或正文或注入模拟),因此仅对记录请求参数有用。这是一个非常狭窄的用例。
async
函数让 fetch
上的拦截器 await
promise 并可能处理响应(模拟、读取等)但是 (在撰写本文时)有一个多余的闭包,并且没有说明如何非破坏性地读取响应主体。它还包含一个导致堆栈溢出的变量别名错误。
这是一个纠正这些问题的最小的完整示例,展示了如何处理参数日志记录、读取正文而不伤害原始调用者
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))
;