如何使用适配器更改 Axios 中的结果状态?
How can I change the result status in Axios with an adapter?
原因
我们正在使用 axios-retry 库,它在内部使用此代码:
axios.interceptors.response.use(null, error => {
因为它只指定错误回调,所以 Axios documentation 表示:
Any status codes that falls outside the range of 2xx cause this function to trigger
不幸的是,我们正在调用一个非RESTful API,它可以 return 200,正文中有一个错误代码,我们需要重试。
我们尝试在 axios-retry 之前添加一个 Axios 拦截器,并在这种情况下更改结果状态;但这并没有触发后续的拦截器错误回调。
所做的工作是指定自定义适配器。然而,这并没有得到很好的记录,我们的代码并没有处理所有情况。
密码
const axios = require('axios');
const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');
const axiosRetry = require('axios-retry');
const myAdapter = async function(config) {
return new Promise((resolve, reject) => {
// Delegate to default http adapter
return httpAdapter(config).then(result => {
// We would have more logic here in the production code
if (result.status === 200) result.status = 500;
settle(resolve, reject, result);
return result;
});
});
}
const axios2 = axios.create({
adapter: myAdapter
});
function isErr(error) {
console.log('retry checking response', error.response.status);
return !error.response || (error.response.status === 500);
}
axiosRetry(axios2, {
retries: 3,
retryCondition: isErr
});
// httpstat.us can return various status codes for testing
axios2.get('http://httpstat.us/200')
.then(result => {
console.log('Result:', result.data);
})
.catch(e => console.error('Service returned', e.message));
这适用于错误情况,打印:
retry checking response 500
retry checking response 500
retry checking response 500
retry checking response 500
Service returned Request failed with status code 500
它在成功案例中也有效(将 URL 更改为 http://httpstat.us/201):
Result: { code: 201, description: 'Created' }
问题
将 URL 更改为 http://httpstat.us/404,但会导致:
(node:19759) UnhandledPromiseRejectionWarning: Error: Request failed with status code 404
at createError (.../node_modules/axios/lib/core/createError.js:16:15)
at settle (.../node_modules/axios/lib/core/settle.js:18:12)
httpAdapter
调用中的 catch
将捕获该错误,但我们如何将其传递到链中?
实现 Axios 适配器的正确方法是什么?
如果有更好的方法来处理这个问题(除了分叉 axios-retry 库),那将是一个可以接受的答案。
更新
一位同事发现在 httpAdapter
调用中执行 .catch(e => reject(e))
(或只是 .catch(reject)
)似乎可以解决问题。但是,我们仍然希望有一个规范的示例来实现包装默认 http 适配器的 Axios 适配器。
这是有效的(在节点中):
const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');
const customAdapter = config =>
new Promise((resolve, reject) => {
httpAdapter(config).then(response => {
if (response.status === 200)
// && response.data contains particular error
{
// log if desired
response.status = 503;
}
settle(resolve, reject, response);
}).catch(reject);
});
// Then do axios.create() and pass { adapter: customAdapter }
// Now set up axios-retry and its retryCondition will be checked
拦截器和自定义错误的解决方法
const axios = require("axios").default;
const axiosRetry = require("axios-retry").default;
axios.interceptors.response.use(async (response) => {
if (response.status == 200) {
const err = new Error("I want to retry");
err.config = response.config; // axios-retry using this
throw err;
}
return response;
});
axiosRetry(axios, {
retries: 1,
retryCondition: (error) => {
console.log("retryCondition");
return false;
},
});
axios
.get("https://example.com/")
.catch((err) => console.log(err.message)); // gonna be here anyway as we'll fail due to interceptor logic
原因
我们正在使用 axios-retry 库,它在内部使用此代码:
axios.interceptors.response.use(null, error => {
因为它只指定错误回调,所以 Axios documentation 表示:
Any status codes that falls outside the range of 2xx cause this function to trigger
不幸的是,我们正在调用一个非RESTful API,它可以 return 200,正文中有一个错误代码,我们需要重试。
我们尝试在 axios-retry 之前添加一个 Axios 拦截器,并在这种情况下更改结果状态;但这并没有触发后续的拦截器错误回调。
所做的工作是指定自定义适配器。然而,这并没有得到很好的记录,我们的代码并没有处理所有情况。
密码
const axios = require('axios');
const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');
const axiosRetry = require('axios-retry');
const myAdapter = async function(config) {
return new Promise((resolve, reject) => {
// Delegate to default http adapter
return httpAdapter(config).then(result => {
// We would have more logic here in the production code
if (result.status === 200) result.status = 500;
settle(resolve, reject, result);
return result;
});
});
}
const axios2 = axios.create({
adapter: myAdapter
});
function isErr(error) {
console.log('retry checking response', error.response.status);
return !error.response || (error.response.status === 500);
}
axiosRetry(axios2, {
retries: 3,
retryCondition: isErr
});
// httpstat.us can return various status codes for testing
axios2.get('http://httpstat.us/200')
.then(result => {
console.log('Result:', result.data);
})
.catch(e => console.error('Service returned', e.message));
这适用于错误情况,打印:
retry checking response 500 retry checking response 500 retry checking response 500 retry checking response 500 Service returned Request failed with status code 500
它在成功案例中也有效(将 URL 更改为 http://httpstat.us/201):
Result: { code: 201, description: 'Created' }
问题
将 URL 更改为 http://httpstat.us/404,但会导致:
(node:19759) UnhandledPromiseRejectionWarning: Error: Request failed with status code 404 at createError (.../node_modules/axios/lib/core/createError.js:16:15) at settle (.../node_modules/axios/lib/core/settle.js:18:12)
httpAdapter
调用中的 catch
将捕获该错误,但我们如何将其传递到链中?
实现 Axios 适配器的正确方法是什么?
如果有更好的方法来处理这个问题(除了分叉 axios-retry 库),那将是一个可以接受的答案。
更新
一位同事发现在 httpAdapter
调用中执行 .catch(e => reject(e))
(或只是 .catch(reject)
)似乎可以解决问题。但是,我们仍然希望有一个规范的示例来实现包装默认 http 适配器的 Axios 适配器。
这是有效的(在节点中):
const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');
const customAdapter = config =>
new Promise((resolve, reject) => {
httpAdapter(config).then(response => {
if (response.status === 200)
// && response.data contains particular error
{
// log if desired
response.status = 503;
}
settle(resolve, reject, response);
}).catch(reject);
});
// Then do axios.create() and pass { adapter: customAdapter }
// Now set up axios-retry and its retryCondition will be checked
拦截器和自定义错误的解决方法
const axios = require("axios").default;
const axiosRetry = require("axios-retry").default;
axios.interceptors.response.use(async (response) => {
if (response.status == 200) {
const err = new Error("I want to retry");
err.config = response.config; // axios-retry using this
throw err;
}
return response;
});
axiosRetry(axios, {
retries: 1,
retryCondition: (error) => {
console.log("retryCondition");
return false;
},
});
axios
.get("https://example.com/")
.catch((err) => console.log(err.message)); // gonna be here anyway as we'll fail due to interceptor logic