我想使用 axios 重试 5xx 请求。我的主要请求位于 try catch 块的中间。我正在使用 axios-retry 库自动重试 3 次。
我使用的网址会故意抛出 503。但是,该请求不会被重试,而是被我的 catch 块捕获。
axiosRetry(axios, {
retries: 3
});
let result;
const url = "https://httpstat.us/503";
const requestOptions = {
url,
method: "get",
headers: {
},
data: {},
};
try {
result = await axios(requestOptions);
} catch (err) {
throw new Error("Failed to retry")
}
}
return result;
axios-retry 使用 axios interceptor 重试 HTTP 请求。它在 then 或 catch 处理请求或响应之前拦截它们。 下面是工作代码片段。
const axios = require('axios');
const axiosRetry = require('axios-retry');
axiosRetry(axios, {
retries: 3, // number of retries
retryDelay: (retryCount) => {
console.log(`retry attempt: ${retryCount}`);
return retryCount * 2000; // time interval between retries
},
retryCondition: (error) => {
// if retry condition is not specified, by default idempotent requests are retried
return error.response.status === 503;
},
});
async function makeHTTPCall() {
const response = await axios({
method: 'GET',
url: 'https://httpstat.us/503',
}).catch((err) => {
if (err.response.status !== 200) {
throw new Error(`API call failed with status code: ${err.response.status} after 3 retry attempts`);
}
});
}
makeHTTPCall();
使用重试
const retry = require('retry');
const operation = retry.operation({
retries: 5,
factor: 3,
minTimeout: 1 * 1000,
maxTimeout: 60 * 1000,
randomize: true,
});
operation.attempt(async (currentAttempt) => {
console.log('sending request: ', currentAttempt, ' attempt');
try {
await axios.put(...);
} catch (e) {
if (operation.retry(e)) { return; }
}
});
您可以使用
axios.interceptors.response.use
来做到这一点。当遇到错误时,您可以使用 axios
递归返回 resolve
请求。然后你可以自己定义条件,每当你想重试时。这是一个简单的axios重试版本,这也是axios-retry所做的一个概念。
const axios = require("axios")
/**
*
* @param {import("axios").AxiosInstance} axios
* @param {Object} options
* @param {number} options.retry_time
* @param {number} options.retry_status_code
*/
const retryWrapper = (axios, options) => {
const max_time = options.retry_time;
const retry_status_code = options.retry_status_code;
let counter = 0;
axios.interceptors.response.use(null, (error) => {
/** @type {import("axios").AxiosRequestConfig} */
const config = error.config
// you could defined status you want to retry, such as 503
// if (counter < max_time && error.response.status === retry_status_code) {
if (counter < max_time) {
counter++
return new Promise((resolve) => {
resolve(axios(config))
})
}
return Promise.reject(error)
})
}
async function main () {
retryWrapper(axios, {retry_time: 3})
const result = await axios.get("https://api.ipify.org?format=json")
console.log(result.data);
}
main()
我还做了一个demo版本来模拟最终请求成功,但之前的请求全部失败。我定义了status_code: 404 我想重试,并设置了3次重试。
const axios = require("axios")
/**
*
* @param {import("axios").AxiosInstance} axios
* @param {Object} options
* @param {number} options.retry_time
* @param {number} options.retry_status_code
*/
const retryWrapper = (axios, options) => {
const max_time = options.retry_time;
const retry_status_code = options.retry_status_code;
let counter = 0;
axios.interceptors.response.use(null, (error) => {
console.log("==================");
console.log(`Counter: ${counter}`);
console.log("Error: ", error.response.statusText);
console.log("==================");
/** @type {import("axios").AxiosRequestConfig} */
const config = error.config
if (counter < max_time && error.response.status === retry_status_code) {
counter++
return new Promise((resolve) => {
resolve(axios(config))
})
}
// ===== this is mock final one is a successful request, you could delete one in usage.
if (counter === max_time && error.response.status === retry_status_code) {
config.url = "https://api.ipify.org?format=json"
return new Promise((resolve) => {
resolve(axios(config))
})
}
return Promise.reject(error)
})
}
async function main () {
retryWrapper(axios, {retry_time: 3, retry_status_code: 404})
const result = await axios.get("http://google.com/not_exist")
console.log(result.data);
}
main()
您将看到以下消息的日志打印。
==================
Counter: 0
Error: Not Found
==================
==================
Counter: 1
Error: Not Found
==================
==================
Counter: 2
Error: Not Found
==================
==================
Counter: 3
Error: Not Found
==================
{ ip: 'x.x.x.x' }
更新 Andrien 答案以防止流程泄漏。一旦请求成功,我们应该停止循环,并且忘记添加增量,因此这意味着它永远不会停止循环。仅当请求失败且最多为 3 时,才会重试。您可以根据您的首选值更改
maxRetries
来增加该值。
new Promise(async (resolve, reject) => {
let retries = 0;
let success = false;
const maxRetries = 3;
while (retries < maxRetries && !success) {
try {
const response = await axios(options);
success = true
resolve(response.data);
} catch (err) {
const status = err?.response?.status || 500;
console.log(`Error Status: ${status}`);
}
retries++
}
console.log(`Too many request retries.`);
reject()
})
有关更多详细信息,请查看 while 循环的文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
接受的解决方案将强制所有失败的 HTTP 请求(使用 axios)重试。 这显然不是任何人默认想要的。
解决方案是为特定的 HTTP 调用创建一个新的 axios 实例,并为其定义重试策略:
import axios from 'axios'
import axiosRetry from 'axios-retry'
async function call(){
const client = axios.create()
axiosRetry(client, { retries: 4, /* ...more options */ })
const response = client.get("https://www.google.com")
}
call()
``
这使用了 Junius L. 引用的
retry
库,但我将其包装在一个承诺中。超时时会拒绝,成功后会解决。
const retry = require('retry');
const sendWithRetry = axiosCall => new Promise((resolve, reject) => {
const operation = retry.operation({
retries: 5,
factor: 3,
minTimeout: 1 * 1000,
maxTimeout: 60 * 1000,
randomize: true,
});
operation.attempt(async (currentAttemptNumber) => {
try {
await axiosCall;
} catch (e) {
if (operation.retry(e)) { return; }
resolve();
}
}, reject);
});
您可以通过不使用其他库来做到这一点。
new Promise(async (resolve, reject) => {
let retries = 0;
const maxRetries = 3;
while (retries < maxRetries) {
try {
const response = await axios(options);
break;
resolve(response.data);
} catch (err) {
const status = err?.response?.status || 500;
console.log(`Error Status: ${status}`);
retries++
}
}
console.log(`Too many request retries.`);
reject()
})
我不知道要寻找什么,然后这篇 stackoverflow 帖子我会说我从每个人那里学到了一点,所以谢谢。这是我的看法
axios.interceptors.response.use(
response => {
return response;
},
async error => {
// @link https://stackoverflow.com/questions/56074531/how-to-retry-5xx-requests-using-axios/75956421#75956421
if (error?.config?.headers?.['X-Retry-Count'] !== undefined) {
console.log('X-Retry-Count', error?.config?.headers?.['X-Retry-Count'])
if (false === isTest || true === isVerbose) {
console.log(error)
}
return error;
}
logResponseSetCookiesForTests(error);
error.response ??= {};
error.response.status ??= 520;
const shouldRetry = (error) => undefined !== error.config && error?.response?.status >= 500 && error?.response?.status < 600
const firstRetry = shouldRetry(error)
if (false === isTest || true === isVerbose) {
console.group("Retrying request ", error.config?.url ?? error.config);
console.log(error);
console.groupEnd();
} else if (isTest) {
console.log('AXIOS ERROR', error.code, error.baseURL, error.config?.url, error.headers, error.data, error.params, error.path, error.response?.status, error.response?.statusText, error.response?.headers, error.response?.data)
if (false === firstRetry) {
throw new Error(error.code + ' ' + error.config?.url)
}
}
if (false === firstRetry) {
console.error("Error in axiosInterceptors.tsx (Not attempting retry)", error);
if (undefined !== error?.response?.data?.TRACE ||
undefined === error?.response?.data?.alert) {
console.log('backend throwable', error?.response?.data || error?.response)
if (undefined !== error?.response?.data
&& Array.isArray(error.response.data)) {
error.response.data.status = error?.response?.status
}
return Promise.reject(error);
}
/* Do something with response error
this changes from project to project depending on how your server uses response codes.
when you can control all errors universally from a single api, return Promise.reject(error);
is the way to go.
*/
HandleResponseCodes(error.response);
return Promise.reject(error);
}
console.warn("Error in axiosInterceptors.tsx - Attempting retry!!!");
const config: AxiosRequestConfig = error.config
// @link https://stackoverflow.com/questions/3561381/custom-http-headers-naming-conventions
let retries = parseInt(config.headers?.['X-Retry-Count'] ?? '0');
const maxRetries = 3;
// todo - handle retries better
while (retries < maxRetries) {
config.headers = {
...config.headers,
'X-Retry-Count': `${++retries}`
}
try {
// @link https://stackoverflow.com/questions/51563821/axios-interceptors-retry-original-request-and-access-original-promise
return axios(config)
} catch (err) {
error = err;
return Promise.reject(error);
}
}
console.log(`Too many request retries.`);
return Promise.reject(error);
});