封装 axios 拦截器实现用户无感刷新 access_token
Author: 图恩Category: 编程开发Views: 667Words: 10615Published: 2024-07-29 ### Optimizing the Processing Logic for the `requests` Array
To address the issue of refreshing `access_token` when multiple requests are initiated simultaneously, the following key logic must be implemented in the code:
---
### **1. Optimizing the `requests` Array Processing Logic**
When multiple requests are initiated concurrently, ensure that all pending requests are correctly executed after `access_token` refresh.
```javascript
let isRefreshing = false;
let requests = []; // Store pending requests
```
**Key Logic:**
- After `refreshToken` succeeds, iterate through the `requests` array and execute callback functions sequentially.
- Clear the array after refresh to avoid reprocessing.
```javascript
instance.interceptors.response.use(
response => response,
error => {
if (!error.response) return Promise.reject(error);
if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) {
const { config } = error;
if (!isRefreshing) {
isRefreshing = true;
return refreshToken().then(res => {
const { access_token } = res.data;
setToken(access_token);
config.headers.Authorization = `Bearer ${access_token}`;
// Execute all pending requests
requests.forEach(cb => cb(access_token));
requests = [];
return instance(config);
}).catch(err => Promise.reject(err)).finally(() => {
isRefreshing = false;
});
} else {
// Save unresolved Promise for subsequent processing
return new Promise(resolve => {
requests.push(token => {
config.headers.Authorization = `Bearer ${token}`;
resolve(instance(config));
});
});
}
}
return Promise.reject(error);
}
);
```
---
### **2. Ensuring All Requests Are Correctly Executed After Refresh**
After `refreshToken` succeeds, ensure all pending request callbacks are executed:
```javascript
requests.forEach(cb => cb(access_token));
requests = [];
```
**Explanation:**
- Each element in `requests` is a callback function (e.g., `cb(access_token)`).
- After refresh, callbacks are executed sequentially.
- Clearing the array prevents duplicate processing.
---
### **3. Handling Unresolved Promises on Token Refresh Failure**
When `refreshToken` fails, save the unresolved Promise to the `requests` array for subsequent handling:
```javascript
return new Promise(resolve => {
requests.push(token => {
config.headers.Authorization = `Bearer ${token}`;
resolve(instance(config));
});
});
```
**Key Points:**
- `resolve` is saved to `requests` for later execution.
- When refresh succeeds, all callbacks are invoked to reissue pending requests.
---
### **4. Preventing Multiple Token Refreshes**
Use the `isRefreshing` flag to control refresh state and avoid simultaneous requests:
```javascript
if (!isRefreshing) {
isRefreshing = true;
return refreshToken().then(...);
}
```
**Explanation:**
- If refresh is in progress, new requests are returned with unresolved Promises to avoid duplicate processing.
---
### **Final Optimized Code**
```javascript
// request.js
import axios from 'axios';
import { getToken, setToken, getRefreshToken } from './auth';
const instance = axios.create({
baseURL: process.env.GATSBY_API_URL,
timeout: 30000,
headers: { 'Content-Type': 'application/json' }
});
let isRefreshing = false;
let requests = [];
instance.interceptors.response.use(
response => response,
error => {
if (!error.response) return Promise.reject(error);
if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) {
const { config } = error;
if (!isRefreshing) {
isRefreshing = true;
return refreshToken().then(res => {
const { access_token } = res.data;
setToken(access_token);
config.headers.Authorization = `Bearer ${access_token}`;
requests.forEach(cb => cb(access_token));
requests = [];
return instance(config);
}).catch(err => Promise.reject(err)).finally(() => {
isRefreshing = false;
});
} else {
return new Promise(resolve => {
requests.push(token => {
config.headers.Authorization = `Bearer ${token}`;
resolve(instance(config));
});
});
}
}
return Promise.reject(error);
}
);
const setHeaderToken = (isNeedToken) => {
const accessToken = isNeedToken ? getToken() : null;
if (isNeedToken) {
instance.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
}
};
export const get = (url, params = {}, isNeedToken = false) => {
setHeaderToken(isNeedToken);
return instance({
method: 'get',
url,
params
});
};
export const post = (url, data, isNeedToken = false) => {
setHeaderToken(isNeedToken);
return instance({
method: 'post',
url,
data
});
};
```
---
### **Summary**
- **Key Logic:** Use the `requests` array to store pending requests, ensuring all are executed after `access_token` refresh.
- **Prevent Duplicate Refreshes:** The `isRefreshing` flag controls refresh state, avoiding simultaneous requests.
- **Handle Failures:** Save unresolved Promises to the array for subsequent processing.
This approach effectively resolves the issue of refreshing `access_token` when multiple requests are initiated, ensuring all requests are correctly handled after token updates.