|
|
@@ -1,19 +1,13 @@
|
|
|
import type { UseFetchOptions } from '#app'
|
|
|
-import type { BasicResponse } from '~/models/common'
|
|
|
-import type { RequestQueueItem, IInterceptor, IConfig, IOption } from './interface'
|
|
|
-import { useLoginStore } from '~/stores/user/login'
|
|
|
-import { getToken, getRefreshToken } from '../auth'
|
|
|
+import type { IInterceptor, IConfig, IOption } from './interface'
|
|
|
+
|
|
|
class Request {
|
|
|
public baseURL: string
|
|
|
public interceptor: IInterceptor
|
|
|
- private isRefreshing = false // 是否正在刷新token,开启请求队列
|
|
|
- private requestQueue: RequestQueueItem[] // 待请求队列
|
|
|
|
|
|
constructor({ baseURL, interceptor }: IConfig) {
|
|
|
this.baseURL = baseURL
|
|
|
this.interceptor = interceptor as IInterceptor
|
|
|
- this.isRefreshing = false
|
|
|
- this.requestQueue = []
|
|
|
}
|
|
|
|
|
|
request<T = BasicResponse>({ url, method, params, data, options }: IOption<T>): Promise<T> {
|
|
|
@@ -28,110 +22,12 @@ class Request {
|
|
|
onResponse: this.interceptor?.onResponse,
|
|
|
onResponseError: this.interceptor?.onResponseError,
|
|
|
}
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- this.requestPipeline(url, newOptions, resolve, reject)
|
|
|
- })
|
|
|
+ return this.requestPipeline(url, newOptions)
|
|
|
}
|
|
|
|
|
|
// 请求管道处理具体细节
|
|
|
- requestPipeline<T = BasicResponse>(
|
|
|
- url: string,
|
|
|
- options: UseFetchOptions<T>,
|
|
|
- resolve: (data: T) => void,
|
|
|
- reject: (data: unknown) => void
|
|
|
- ): void {
|
|
|
- const token = getToken()
|
|
|
- const newOptions = {
|
|
|
- ...options,
|
|
|
- headers: {
|
|
|
- Authorization: token ? `Bearer ${token}` : '',
|
|
|
- ...options?.headers,
|
|
|
- },
|
|
|
- }
|
|
|
- $fetch<T>(url, newOptions as any)
|
|
|
- .then((res) => {
|
|
|
- resolve(res as T)
|
|
|
- })
|
|
|
- .catch((error) => {
|
|
|
- if (error.status === 401) {
|
|
|
- if (!this.isRefreshing) {
|
|
|
- this.isRefreshing = true
|
|
|
- this.refreshToken()
|
|
|
- }
|
|
|
- this.addRequestQueueForRefreshToken<T>(url, options, resolve, reject)
|
|
|
- } else {
|
|
|
- reject(error)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 刷新token
|
|
|
- refreshToken() {
|
|
|
- const loginStore = useLoginStore()
|
|
|
- this.postRefreshTokenFunc()
|
|
|
- .then((res) => {
|
|
|
- if (res.data) {
|
|
|
- const data = res.data
|
|
|
- loginStore.updateToken(data)
|
|
|
- this.requestQueueStartAfterRefreshToken()
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- loginStore.logout()
|
|
|
- navigateTo({ path: '/' })
|
|
|
-
|
|
|
- // ElMessage.error('登录已失效,需要重新登录')
|
|
|
- // navigateTo({ path: '/login' })
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- this.isRefreshing = false
|
|
|
- })
|
|
|
- }
|
|
|
- postRefreshTokenFunc(): Promise<BasicResponse> {
|
|
|
- const data = {
|
|
|
- clientId: getCanvasFingerprint(),
|
|
|
- }
|
|
|
- const token = getRefreshToken()
|
|
|
- return $fetch(this.baseURL + '/auth/refresh', {
|
|
|
- method: 'post',
|
|
|
- body: data,
|
|
|
- headers: {
|
|
|
- Authorization: `Bearer ${token}`,
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- },
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 添加请求到等待队列
|
|
|
- addRequestQueueForRefreshToken<T = BasicResponse>(
|
|
|
- url: string,
|
|
|
- options: UseFetchOptions<T>,
|
|
|
- resolve: (data: T) => void,
|
|
|
- reject: (data: unknown) => void
|
|
|
- ): void {
|
|
|
- this.requestQueue.push({
|
|
|
- url,
|
|
|
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
- // @ts-ignore
|
|
|
- options,
|
|
|
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
- // @ts-ignore
|
|
|
- resolve,
|
|
|
- reject,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 刷新token成功等待队列开始请求
|
|
|
- requestQueueStartAfterRefreshToken(): void {
|
|
|
- let requestQueueItem = this.requestQueue.pop()
|
|
|
-
|
|
|
- while (requestQueueItem) {
|
|
|
- const { options, url, resolve, reject } = requestQueueItem
|
|
|
-
|
|
|
- this.requestPipeline(url, options, resolve, reject)
|
|
|
-
|
|
|
- requestQueueItem = this.requestQueue.pop()
|
|
|
- }
|
|
|
+ requestPipeline<T = any>(url: string, options: UseFetchOptions<T>): Promise<T> {
|
|
|
+ return $fetch<T>(url, options as any)
|
|
|
}
|
|
|
}
|
|
|
|