✨
This commit is contained in:
153
src/api/AxiosHelper.ts
Normal file
153
src/api/AxiosHelper.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import qs from 'qs'
|
||||
import Axios, { HttpStatusCode } from 'axios'
|
||||
import type { AxiosInstance, AxiosResponse, CreateAxiosDefaults } from 'axios'
|
||||
import type { AccessToken, AccessTokenResponse } from './models/AccessToken'
|
||||
import { type AntiForgeryTokenResponse, type AntiForgeryToken } from './models/AntiForgeryToken'
|
||||
import { type ProblemDetails } from './ProblemDetails'
|
||||
|
||||
class AxiosHelper {
|
||||
private readonly REFRESH_TOKEN_URL: string = '/common/refreshtoken'
|
||||
private constructor(config?: CreateAxiosDefaults) {
|
||||
this.axios = Axios.create({
|
||||
withCredentials: true,
|
||||
baseURL: import.meta.env.VITE_API_BASE,
|
||||
paramsSerializer: (params) => {
|
||||
return qs.stringify(params, {
|
||||
indices: false,
|
||||
arrayFormat: 'repeat',
|
||||
})
|
||||
},
|
||||
...config,
|
||||
})
|
||||
|
||||
this.axios.interceptors.request.use(async (config) => {
|
||||
if (this.accessToken?.token) {
|
||||
config.headers.Authorization = `Bearer ${this.accessToken?.token}`
|
||||
}
|
||||
if (this.csrfToken) config.headers[this.csrfToken.headerName] = this.csrfToken.token
|
||||
return config
|
||||
})
|
||||
|
||||
this.axios.interceptors.response.use(null, async (err) => {
|
||||
if (err.response?.status === HttpStatusCode.Unauthorized) {
|
||||
if (err.config.url == this.REFRESH_TOKEN_URL) {
|
||||
window.location.href = '/signin'
|
||||
return
|
||||
}
|
||||
const succed = await this.refreshAccessToken()
|
||||
if (succed) return await this.axios(err.config)
|
||||
else window.location.href = '/signin'
|
||||
} else if (err.response?.status === HttpStatusCode.BadRequest) {
|
||||
const details = err.response.data as ProblemDetails
|
||||
|
||||
if (details.code == 1000) {
|
||||
const succed = await this.refreshCsrfToken()
|
||||
if (succed) return await this.axios(err.config)
|
||||
else throw Error('网络错误')
|
||||
} else if (details.code == 1001) {
|
||||
throw Error(details.detail || '')
|
||||
} else {
|
||||
throw Error(details.title || '')
|
||||
}
|
||||
} else {
|
||||
throw Error('网络错误')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private accessToken: AccessToken | null = null
|
||||
private csrfToken: AntiForgeryToken | null = null
|
||||
|
||||
private axios: AxiosInstance
|
||||
|
||||
private static instance: AxiosHelper
|
||||
|
||||
static getInstance(config?: CreateAxiosDefaults): AxiosHelper {
|
||||
if (!this.instance) {
|
||||
this.instance = new AxiosHelper(config)
|
||||
}
|
||||
|
||||
return this.instance
|
||||
}
|
||||
|
||||
private async refreshCsrfToken(): Promise<boolean> {
|
||||
const csrfResp = await AxiosHelper.getInstance().post<AntiForgeryTokenResponse>(
|
||||
'/common/antiforgery-token',
|
||||
)
|
||||
|
||||
if (csrfResp.isSucced) {
|
||||
this.csrfToken = csrfResp.data
|
||||
} else {
|
||||
throw Error(csrfResp.message || '服务器错误,请刷新重试')
|
||||
}
|
||||
return csrfResp.isSucced
|
||||
}
|
||||
|
||||
private async refreshAccessToken(): Promise<boolean> {
|
||||
const tokenResp = await AxiosHelper.getInstance().post<AccessTokenResponse>(
|
||||
this.REFRESH_TOKEN_URL,
|
||||
)
|
||||
|
||||
if (tokenResp.isSucced) {
|
||||
this.accessToken = tokenResp.data
|
||||
this.autoRefreshToken()
|
||||
}
|
||||
|
||||
return tokenResp.isSucced
|
||||
}
|
||||
|
||||
private autoRefreshToken() {
|
||||
if (this.accessToken?.expiresIn) {
|
||||
setTimeout(
|
||||
async () => {
|
||||
await this.refreshAccessToken()
|
||||
},
|
||||
(this.accessToken.expiresIn - 10) * 1000,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private handleResult<T>(result: AxiosResponse<T, unknown, unknown>): T {
|
||||
if (result.status === HttpStatusCode.Ok) {
|
||||
return result.data
|
||||
} else {
|
||||
console.error(result)
|
||||
throw new Error(`${result.status}:${result.statusText}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功后调用
|
||||
* @param accessToken
|
||||
*/
|
||||
async initToken(accessToken: AccessToken) {
|
||||
this.accessToken = accessToken
|
||||
if (this.accessToken?.expiresIn) {
|
||||
this.autoRefreshToken()
|
||||
}
|
||||
}
|
||||
|
||||
async initCsrfToken(csrfToken: AntiForgeryToken) {
|
||||
this.csrfToken = csrfToken
|
||||
}
|
||||
|
||||
async get<TOut, TIn = unknown>(path: string, data?: TIn): Promise<TOut> {
|
||||
const result = await AxiosHelper.getInstance().axios.get(path, {
|
||||
params: data,
|
||||
})
|
||||
|
||||
return this.handleResult(result)
|
||||
}
|
||||
|
||||
async post<TOut, TIn = unknown>(path: string, data?: TIn): Promise<TOut> {
|
||||
const result = await AxiosHelper.getInstance().axios.post(path, data)
|
||||
return this.handleResult(result)
|
||||
}
|
||||
|
||||
async postFormData<TOut, TIn = unknown>(path: string, data?: TIn): Promise<TOut> {
|
||||
const result = await AxiosHelper.getInstance().axios.postForm(path, data)
|
||||
return this.handleResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
export { AxiosHelper }
|
||||
Reference in New Issue
Block a user