1. 请求拦截器和响应拦截器分别有什么作用?
请求拦截器管请求发出去之前的事,响应拦截器管请求回来之后的事。
继续追问:请求拦截器一般做什么?
最常见的就是统一加 Token、公共请求头和请求配置。 例如:
javascript
axios.interceptors.request.use(config => {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
继续追问:响应拦截器一般做什么?
主要是统一处理响应数据和错误。 比如:
javascript
axios.interceptors.response.use(
response => {
return response.data;
},
error => {
if (error.response?.status === 401) {
// Token 失效
}
return Promise.reject(error);
}
);
2. 为什么要封装一层请求,而不是每个地方直接用 Axios?
就是把重复的请求逻辑统一掉,让业务代码只管调接口。
比如业务代码只需要:
javascript
const user = await getUser();
而不用每个地方都写:
text
加 Token
设置 Header
处理 401
处理错误
设置超时
3. Token 过期了怎么办?
收到 401 后刷新 Token,刷新成功再把原请求重新发一次。
流程:
text
请求
↓
401
↓
刷新 Token
↓
成功
↓
重新请求原接口
4. 如果同时有很多请求都返回 401 呢?
不能让每个请求都去刷新 Token,只能同时刷新一次,其他请求等它。
例如:
text
请求 A ──→ 401 ──┐
请求 B ──→ 401 ──┤
请求 C ──→ 401 ──┤
↓
刷新 Token
↓
成功
↙ ↓ ↘
A重试 B重试 C重试
常见做法是共享一个 Promise:
javascript
let refreshPromise = null;
async function refreshTokenOnce() {
if (!refreshPromise) {
refreshPromise = refreshToken()
.finally(() => {
refreshPromise = null;
});
}
return refreshPromise;
}
核心就是:
同一时间只能有一个刷新请求。
5. Token 刷新失败怎么办?
刷新失败就不能继续重试了,一般清掉登录状态,让用户重新登录。
否则就会出现:
text
401
↓
刷新失败
↓
重新请求
↓
401
↓
继续刷新
↓
......
所以必须有明确的失败出口。
6. 所有请求失败都应该自动重试吗?
不应该,要看失败原因和请求本身能不能安全重试。
例如:
text
GET 查询
→ 一般可以考虑重试
POST 创建订单
→ 不能随便重试
支付请求
→ 更不能简单自动重试
因为有些请求重复执行会产生实际业务影响。
7. 怎么做请求重试?
一般会限制重试次数,而且只对明确可以重试的错误重试。
例如:
javascript
async function requestWithRetry(request, maxRetry = 3) {
let count = 0;
while (true) {
try {
return await request();
} catch (error) {
count++;
if (count > maxRetry || !canRetry(error)) {
throw error;
}
await sleep(1000 * count);
}
}
}
这里还可以继续追问:
为什么不能无限重试?
因为网络异常可能一直存在,无限重试会把问题放大。
8. 请求去重是怎么做的?
核心就是给请求生成一个唯一 Key,相同请求正在执行时就不要重复发。
例如:
text
method + url + params + body
↓
requestKey
第一次:
text
GET /user → 发请求
第二次:
text
GET /user → 发现相同请求正在执行
→ 复用 / 取消
具体选择哪种,要看业务需求。
9. 怎么取消请求?
现在一般直接用 AbortController。
javascript
const controller = new AbortController();
fetch('/api/user', {
signal: controller.signal
});
// 取消
controller.abort();
典型场景就是搜索:
text
输入 A
↓
请求 A
输入 AB
↓
取消 A
↓
请求 AB
输入 ABC
↓
取消 AB
↓
请求 ABC
避免旧请求回来以后把新数据覆盖掉。
10. 所有错误都在响应拦截器里弹 Toast,可以吗?
不建议,请求层负责识别和处理错误,具体怎么提示最好由业务决定。
因为不同接口的错误处理可能不一样:
text
有的 → Toast
有的 → 页面展示
有的 → 静默处理
有的 → 自己处理
如果请求层直接绑定 UI,就容易变成:
text
请求层
↓
Axios
↓
Toast
↓
具体 UI 框架
耦合会比较重。
11. 请求拦截器是不是只能 Axios 有?
不是,拦截请求是一个设计思路,不是 Axios 独有的功能。
Axios 自己提供了拦截器;如果用 fetch,也可以自己封装:
javascript
async function request(url, options) {
const config = beforeRequest(url, options);
const response = await fetch(
config.url,
config.options
);
return afterResponse(response);
}
一道题怎么连续追?
面试官:
请求拦截器和响应拦截器有什么作用?
你先答:请求拦截器管请求发出去之前的事,响应拦截器管请求回来之后的事。
面试官继续:
Token 怎么处理?
请求拦截器统一加 Token。
Token 过期呢?
收到 401 就刷新 Token,成功后重发原请求。
如果同时 10 个请求都 401 呢?
不能让 10 个请求一起刷新,只能刷新一次,其他请求等刷新结果。
刷新失败呢?
清掉登录状态,让用户重新登录,不能继续重试。
那请求重试呢?
只重试明确可以安全重试的请求,而且要限制次数。
你还会考虑什么?
请求取消、请求去重、错误统一处理,这些都可以根据业务需要加。
这就比单纯回答"请求拦截器加 Token,响应拦截器处理错误"明显高一个层次。