zhangwei
3 天以前 cfb72890898236afab5e691fc11abd620b3f157f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/**
 * 当前版本:v1.4.0
 * 使用描述:https://editor.swagger.io 代码生成 typescript-axios 辅组工具库
 * 依赖说明:适配 axios 版本:v0.21.4
 * 视频教程:https://www.bilibili.com/video/BV1EW4y1C71D
 */
 
import globalAxios, { AxiosInstance } from 'axios';
import { Configuration } from '../api-services';
import { BASE_PATH } from '../api-services/base';
 
import { ElMessage } from 'element-plus';
import { Local, Session } from '../utils/storage';
import { useUserInfo } from "/@/stores/userInfo";
import {useRoute, useRouter} from "vue-router";
 
// 接口服务器配置
export const serveConfig = new Configuration({
    basePath: window.__env__.VITE_API_URL,
});
 
// token 键定义
export const accessTokenKey = 'access-token';
export const refreshAccessTokenKey = `x-${accessTokenKey}`;
 
// 获取 token
export const getToken = () => {
    return Local.get(accessTokenKey);
};
 
// 获取请求头 token
export const getHeader = () => {
    return { authorization: 'Bearer ' + getToken() };
};
 
// 清除 token
export const clearAccessAfterReload = () => {
    clearTokens();
    // 刷新浏览器
    window.location.reload();
};
 
// 清除 token
export const clearTokens = () => {
    Local.remove(accessTokenKey);
    Local.remove(refreshAccessTokenKey);
    Session.clear();
};
 
// axios 默认实例
export const axiosInstance: AxiosInstance = globalAxios;
 
// 这里可以配置 axios 更多选项 =========================================
axiosInstance.defaults.timeout = 1000 * 60 * 10; // 设置超时,默认 10 分钟
 
// axios 请求拦截
axiosInstance.interceptors.request.use(
    (conf) => {
        // 获取本地的 token或session中的token
        const accessToken = Local.get(accessTokenKey) ? Local.get(accessTokenKey) : Session.get('token');
        if (accessToken) {
            // 将 token 添加到请求报文头中
            conf.headers!['Authorization'] = `Bearer ${accessToken}`;
 
            // 判断 accessToken 是否过期
            const jwt: any = decryptJWT(accessToken);
            const exp = getJWTDate(jwt.exp as number);
 
            // token 已经过期
            if (new Date() >= exp) {
                // 获取刷新 token
                const refreshAccessToken = Local.get(refreshAccessTokenKey);
 
                // 携带刷新 token
                if (refreshAccessToken) {
                    conf.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
                }
            }
        }
 
        // 这里编写请求拦截代码 =========================================
 
        // 获取前端设置的语言
        const globalI18n = Local.get('themeConfig')?.globalI18n;
        if (globalI18n) {
            // 添加到请求报文头中
            conf.headers!['Accept-Language'] = globalI18n;
        }
        return conf;
    },
    (error) => {
        // 处理请求错误
        if (error.request) {
            ElMessage.error(error);
        }
 
        // 请求错误代码及自定义处理
        ElMessage.error(error);
 
        return Promise.reject(error);
    }
);
 
// axios 响应拦截
axiosInstance.interceptors.response.use(
    (res) => {
        // 获取状态码和返回数据
        var status = res.status;
        var serve = res.data;
 
        // 处理 401
        if (status === 401) {
            clearAccessAfterReload();
        }
 
        // 处理未进行规范化处理的
        if (status >= 400) {
            throw new Error(res.statusText || 'Request Error.');
        }
 
        // 处理规范化结果错误
        if (serve && serve.hasOwnProperty('errors') && serve.errors) {
            throw new Error(JSON.stringify(serve.errors || 'Request Error.'));
        }
 
        // 读取响应报文头 token 信息
        var accessToken = res.headers[accessTokenKey];
        var refreshAccessToken = res.headers[refreshAccessTokenKey];
 
        // 判断是否是无效 token
        if (accessToken === 'invalid_token') {
            clearAccessAfterReload();
        }
        // 判断是否存在刷新 token,如果存在则存储在本地
        else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
            Local.set(accessTokenKey, accessToken);
            Local.set(refreshAccessTokenKey, refreshAccessToken);
        }
 
        // 响应拦截及自定义处理
        if (serve.code === 401) {
            clearAccessAfterReload();
        } else if (serve.code === undefined) {
            return Promise.resolve(res);
        } else if (serve.code !== 200) {
            var message;
            // 判断 serve.message 是否为对象
            if (serve.message && typeof serve.message == 'object') {
                message = JSON.stringify(serve.message);
            } else {
                message = serve.message;
            }
            // 用户自定义处理异常
            if (!res.config?.customCatch) {
                ElMessage.error(message);
            }
            throw new Error(message);
        }
 
        return res;
    },
    (error) => {
        // 处理响应错误
        if (error.response) {
            if (error.response.status === 401) {
                clearAccessAfterReload();
            }
        }
 
        // 用户自定义处理异常
        if (!error.config?.customCatch) {
            // 响应错误代码及自定义处理
            ElMessage.error(error);
        }
 
        return Promise.reject(error);
    }
);
 
/**
 * 包装 Promise 并返回 [Error, any]
 * @param promise Promise 方法
 * @param errorExt 自定义错误信息(拓展)
 * @returns [Error, any]
 */
export function feature<T, U = Error>(promise: Promise<T>, errorExt?: object): Promise<[U, undefined] | [null, T]> {
    return promise
        .then<[null, T]>((data: T) => [null, data])
        .catch<[U, undefined]>((err: U) => {
            if (errorExt) {
                const parsedError = Object.assign({}, err, errorExt);
                return [parsedError, undefined];
            }
 
            return [err, undefined];
        });
}
 
/**
 * 获取/创建服务 API 实例
 * @param apiType BaseAPI 派生类型
 * @param configuration 服务器配置对象
 * @param basePath 服务器地址
 * @param axiosObject axios 实例
 * @returns 服务API 实例
 */
export function getAPI<T>(
    // eslint-disable-next-line no-unused-vars
    apiType: new (configuration?: Configuration, basePath?: string, axiosInstance?: AxiosInstance) => T,
    configuration: Configuration = serveConfig,
    basePath: string = BASE_PATH,
    axiosObject: AxiosInstance = axiosInstance
) {
    return new apiType(configuration, basePath, axiosObject);
}
 
/**
 * 解密 JWT token 的信息
 * @param token jwt token 字符串
 * @returns <any>object
 */
export function decryptJWT(token: string): any {
    token = token.replace(/_/g, '/').replace(/-/g, '+');
    var json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
    return JSON.parse(json);
}
 
/**
 * 将 JWT 时间戳转换成 Date
 * @description 主要针对 `exp`,`iat`,`nbf`
 * @param timestamp 时间戳
 * @returns Date 对象
 */
export function getJWTDate(timestamp: number): Date {
    return new Date(timestamp * 1000);
}
 
/**
 * 实现异步延迟
 * @param delay 延迟时间(毫秒)
 * @returns
 */
export function sleep(delay: number) {
    return new Promise((resolve) => setTimeout(resolve, delay));
}