Skip to content

Commit fbe7ddb

Browse files
committed
fix(projects): fix refresh token when meet multi requests
1 parent 92e3cec commit fbe7ddb

File tree

4 files changed

+36
-26
lines changed

4 files changed

+36
-26
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ VITE_SERVICE_LOGOUT_CODES=8888,8889
3838
VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778
3939

4040
# token expired codes of backend service, when the code is received, it will refresh the token and resend the request
41-
VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998
41+
VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998,3333
4242

4343
# when the route mode is static, the defined super role
4444
VITE_STATIC_SUPER_ROLE=R_SUPER

src/service/request/index.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { store } from '@/store';
55
import { $t } from '@/locales';
66
import { localStg } from '@/utils/storage';
77
import { getServiceBaseURL } from '@/utils/service';
8-
import { handleRefreshToken, showErrorMsg } from './shared';
8+
import { getAuthorization, handleExpiredRequest, showErrorMsg } from './shared';
99
import type { RequestInstanceState } from './type';
1010

1111
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
@@ -20,12 +20,8 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
2020
},
2121
{
2222
async onRequest(config) {
23-
const { headers } = config;
24-
25-
// set token
26-
const token = localStg.get('token');
27-
const Authorization = token ? `Bearer ${token}` : null;
28-
Object.assign(headers, { Authorization });
23+
const Authorization = getAuthorization();
24+
Object.assign(config.headers, { Authorization });
2925

3026
return config;
3127
},
@@ -80,18 +76,18 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
8076
return null;
8177
}
8278

79+
// when the backend response code is in `expiredTokenCodes`, it means the token is expired, and refresh token
80+
// the api `refreshToken` can not return error code in `expiredTokenCodes`, otherwise it will be a dead loop, should return `logoutCodes` or `modalLogoutCodes`
8381
// when the backend response code is in `expiredTokenCodes`, it means the token is expired, and refresh token
8482
// the api `refreshToken` can not return error code in `expiredTokenCodes`, otherwise it will be a dead loop, should return `logoutCodes` or `modalLogoutCodes`
8583
const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || [];
86-
if (expiredTokenCodes.includes(responseCode) && !request.state.isRefreshingToken) {
87-
request.state.isRefreshingToken = true;
88-
89-
const refreshConfig = await handleRefreshToken(response.config);
90-
91-
request.state.isRefreshingToken = false;
84+
if (expiredTokenCodes.includes(responseCode)) {
85+
const success = await handleExpiredRequest(request.state);
86+
if (success) {
87+
const Authorization = getAuthorization();
88+
Object.assign(response.config.headers, { Authorization });
9289

93-
if (refreshConfig) {
94-
return instance.request(refreshConfig) as Promise<AxiosResponse>;
90+
return instance.request(response.config) as Promise<AxiosResponse>;
9591
}
9692
}
9793

src/service/request/shared.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,47 @@
1-
import type { AxiosRequestConfig } from 'axios';
21
import { resetStore } from '@/store/slice/auth';
32
import { store } from '@/store';
43
import { localStg } from '@/utils/storage';
54
import { fetchRefreshToken } from '../api';
65
import type { RequestInstanceState } from './type';
76

7+
export function getAuthorization() {
8+
const token = localStg.get('token');
9+
const Authorization = token ? `Bearer ${token}` : null;
10+
11+
return Authorization;
12+
}
13+
814
/**
915
* refresh token
1016
*
1117
* @param axiosConfig - request config when the token is expired
1218
*/
13-
export async function handleRefreshToken(axiosConfig: AxiosRequestConfig) {
19+
export async function handleRefreshToken() {
1420
const refreshToken = localStg.get('refreshToken') || '';
1521
const { error, data } = await fetchRefreshToken(refreshToken);
1622
if (!error) {
1723
localStg.set('token', data.token);
1824
localStg.set('refreshToken', data.refreshToken);
25+
return true;
26+
}
1927

20-
const config = { ...axiosConfig };
21-
if (config.headers) {
22-
config.headers.Authorization = data.token;
23-
}
28+
store.dispatch(resetStore());
2429

25-
return config;
30+
return false;
31+
}
32+
33+
export async function handleExpiredRequest(state: RequestInstanceState) {
34+
if (!state.refreshTokenFn) {
35+
state.refreshTokenFn = handleRefreshToken();
2636
}
2737

28-
store.dispatch(resetStore());
38+
const success = await state.refreshTokenFn;
39+
40+
setTimeout(() => {
41+
state.refreshTokenFn = null;
42+
}, 1000);
2943

30-
return null;
44+
return success;
3145
}
3246

3347
export function showErrorMsg(state: RequestInstanceState, message: string) {

src/service/request/type.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export interface RequestInstanceState {
22
/** whether the request is refreshing token */
3-
isRefreshingToken: boolean;
3+
refreshTokenFn: Promise<boolean> | null;
44
/** the request error message stack */
55
errMsgStack: string[];
66
}

0 commit comments

Comments
 (0)