| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- import { v4 as uuid_v4 } from 'uuid'
- import { FetchClient, type FetchResponse } from './fetchClient'
- import { useUserStore } from '@/stores/user'
- import { jumpToSignin } from '@/utils/casdoor/casdoor'
- import { refreshToken } from './auth'
- const baseUrl = import.meta.env.BASE_URL
- export interface Response<T = any> {
- code: number
- message: string
- data: T
- traceId: string
- timestamp: number
- }
- const http = new FetchClient(baseUrl + 'api', 60 * 1000) // 超时时间设置为60s
- let isRefreshing = false
- let pendingQueue: (() => void)[] = []
- async function handleTokenExpired(
- response: FetchResponse<Response<any>>,
- ): Promise<FetchResponse<Response<any>>> {
- if (!('retry' in response)) {
- return response
- }
- // 原始请求包装成函数
- const originalRequest = response.retry
- if (originalRequest === undefined) {
- return response
- }
- // 如果已经在刷新,挂起请求
- if (isRefreshing) {
- return new Promise<FetchResponse<Response<any>>>((resolve, reject) => {
- pendingQueue.push(() => originalRequest().then(resolve).catch(reject))
- })
- }
- isRefreshing = true
- try {
- // 调用刷新接口
- const res = await refreshToken()
- const userStore = useUserStore()
- userStore.setToken(res)
- // 刷新完成,重放队列请求
- await Promise.all(pendingQueue.map((fn) => fn()))
- pendingQueue = []
- // 重新执行当前请求
- return originalRequest()
- } catch (err) {
- handleRefreshFailed()
- throw err
- } finally {
- isRefreshing = false
- }
- }
- function handleRefreshFailed() {
- const userStore = useUserStore()
- userStore.setToken(undefined)
- pendingQueue = []
- isRefreshing = false
- console.log('jumpToSignin')
- // 跳转登录页
- jumpToSignin()
- }
- // 设置拦截器(可选)
- http.setRequestInterceptor((config) => {
- const userStore = useUserStore()
- // 自动加 token
- const token = userStore.token
- if (token) {
- config.headers = {
- ...config.headers,
- Authorization: `Bearer ${token}`,
- 'X-Trace-Id': uuid_v4(),
- }
- }
- return config
- })
- http.setResponseInterceptor(async (response: FetchResponse<Response<any>>): Promise<any> => {
- // 全局响应处理,比如统一格式校验
- if (!response.ok) {
- console.log({ response })
- switch (response.status) {
- case 401: // Unauthorized Token过期
- // 刷新Token
- response = await handleTokenExpired(response)
- if (!response.ok) {
- console.log({ response })
- throw new Error(response.data)
- }
- break
- case 403: // Forbidden 刷新Token失败
- // 重新登录
- handleRefreshFailed()
- break
- case 204: // No Content
- break
- default:
- console.log({ response })
- throw new Error(response.data)
- }
- return response.data
- }
- return response.data.data
- })
- export default http
|