示例说明
重试最容易踩的坑是对非幂等请求自动重试造成重复扣款。这个拦截器把幂等性判断显式写进重试条件:只有 GET、HEAD、PUT、DELETE 这类可安全重复的方法,且响应属于限流或网关瞬态错误时才重试。attempt 计数挂在 config 上随请求对象流转,避免外部状态。
技术要点
- 幂等方法白名单是重试的前置门槛
- 瞬态状态码白名单隔离业务错误
- 2 的次幂乘基数生成递增退避
代码解读
拦截器的错误分支从 error.config 取回请求配置;__retries 字段初始化为 0,每次重试自增并计算 1 秒、2 秒、4 秒递增的延迟。任何条件不满足都直接 reject 保留原始错误语义。重试调用 client(config) 会重新走完整的请求链,包括其他拦截器。
使用建议
若服务端支持 Idempotency-Key,非幂等方法也可以安全重试。429 响应应优先读取 Retry-After 头而不是固定退避。重试日志要带尝试序号,避免排障时误判为多次独立失败。
代码示例
import axios from 'axios';
const client = axios.create({
baseURL: 'https://api.example.com',
timeout: 8000,
headers: { 'x-client': 'batch-sync/1.2' },
});
client.interceptors.response.use(
(res) => res,
async (error) => {
const config = error.config ?? {};
const retries = config.__retries ?? 0;
const idempotent = ['GET', 'HEAD', 'PUT', 'DELETE'].includes(config.method);
const transient = error.response
&& [429, 502, 503, 504].includes(error.response.status);
if (!idempotent || !transient || retries >= 3) {
return Promise.reject(error);
}
config.__retries = retries + 1;
const delay = 2 ** config.__retries * 500;
await new Promise((r) => setTimeout(r, delay));
return client(config);
},
);
const data = await client.get('/catalog?page=2');
console.log('items:', data.data.items.length);