接口调试
This commit is contained in:
parent
de6dfb5e20
commit
ced65527d5
58
src/utils/request/cancel.ts
Normal file
58
src/utils/request/cancel.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import type { AxiosRequestConfig, Canceler } from 'axios'
|
||||
import axios from 'axios'
|
||||
|
||||
// Used to store the identification and cancellation function of each request
|
||||
let pendingMap = new Map<string, Canceler>()
|
||||
|
||||
const getPendingUrl = (config: AxiosRequestConfig) => [config.method, config.url].join('&')
|
||||
|
||||
export class AxiosCanceler {
|
||||
/**
|
||||
* Add request
|
||||
* @param {Object} config
|
||||
*/
|
||||
addPending(config: AxiosRequestConfig): void {
|
||||
this.removePending(config)
|
||||
const url = getPendingUrl(config)
|
||||
config.cancelToken = config.cancelToken
|
||||
|| new axios.CancelToken((cancel) => {
|
||||
if (!pendingMap.has(url)) {
|
||||
// If there is no current request in pending, add it
|
||||
pendingMap.set(url, cancel)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Clear all pending
|
||||
*/
|
||||
removeAllPending(): void {
|
||||
pendingMap.forEach((cancel) => {
|
||||
cancel?.()
|
||||
})
|
||||
pendingMap.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Removal request
|
||||
* @param {Object} config
|
||||
*/
|
||||
removePending(config: AxiosRequestConfig): void {
|
||||
const url = getPendingUrl(config)
|
||||
|
||||
if (pendingMap.has(url)) {
|
||||
// If there is a current request identifier in pending,
|
||||
// the current request needs to be cancelled and removed
|
||||
const cancel = pendingMap.get(url)
|
||||
cancel && cancel(url)
|
||||
pendingMap.delete(url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: reset
|
||||
*/
|
||||
reset(): void {
|
||||
pendingMap = new Map<string, Canceler>()
|
||||
}
|
||||
}
|
||||
208
src/utils/request/core.ts
Normal file
208
src/utils/request/core.ts
Normal file
@ -0,0 +1,208 @@
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { AxiosCanceler } from './cancel'
|
||||
import router from '@/router'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { HiAesUtil } from './HiAesUtil.ts'
|
||||
const encryptionKey = 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx'
|
||||
function redirectLogin() {
|
||||
localStorage.removeItem('Authorization')
|
||||
router.push('/')
|
||||
}
|
||||
export interface ExtraConfig {
|
||||
/**
|
||||
* 默认值 1 错误等级 0-忽略,1-warning,2-error
|
||||
*/
|
||||
errorLevel?: 0 | 1 | 2
|
||||
/**
|
||||
* 默认true 是否携带token
|
||||
*/
|
||||
withToken?: boolean
|
||||
/**
|
||||
* 默认true 是否处理响应
|
||||
*/
|
||||
handleResponse?: boolean
|
||||
/**
|
||||
* 默认false 是否取消重复请求
|
||||
*/
|
||||
cancelRepetition?: boolean
|
||||
/**
|
||||
* 默认false 是否返回axios完整响应
|
||||
*/
|
||||
originResponseData?: boolean
|
||||
/**
|
||||
* 默认token 存储key
|
||||
*/
|
||||
tokenKey?: string
|
||||
/**
|
||||
* 获取token的方法
|
||||
*/
|
||||
getToken?: () => string
|
||||
/**
|
||||
* 自定义header方法,此方法返回的header会覆盖默认的header
|
||||
*/
|
||||
formatHeader?: (header: Record<string, string>) => Record<string, string>
|
||||
}
|
||||
|
||||
export interface RequestConfig extends AxiosRequestConfig {
|
||||
extraConfig?: ExtraConfig
|
||||
}
|
||||
|
||||
interface ResponseType extends AxiosResponse {
|
||||
config: RequestConfig
|
||||
}
|
||||
|
||||
export default class Request {
|
||||
public axiosInstance: AxiosInstance
|
||||
|
||||
private config: RequestConfig
|
||||
|
||||
constructor(config: RequestConfig) {
|
||||
this.config = config
|
||||
this.axiosInstance = axios.create(config)
|
||||
this.init()
|
||||
}
|
||||
|
||||
static defaultConfig: Required<ExtraConfig> = {
|
||||
errorLevel: 2,
|
||||
withToken: true,
|
||||
handleResponse: true,
|
||||
cancelRepetition: false,
|
||||
originResponseData: false,
|
||||
tokenKey: 'token',
|
||||
formatHeader: (headers) => {
|
||||
return headers
|
||||
},
|
||||
getToken: () => '',
|
||||
}
|
||||
|
||||
private errorReport(lv: number, message: string) {
|
||||
toast(message)
|
||||
}
|
||||
|
||||
private init() {
|
||||
const axiosCanceler = new AxiosCanceler()
|
||||
|
||||
this.axiosInstance.interceptors.request.use(
|
||||
(config: RequestConfig) => {
|
||||
const mergeExtraConfig = {
|
||||
...Request.defaultConfig,
|
||||
...this.config.extraConfig,
|
||||
...config.extraConfig,
|
||||
}
|
||||
|
||||
if (config.data && !(config.data instanceof FormData)) {
|
||||
const plainText = JSON.stringify(config.data)
|
||||
// 加密后,config.data 会变成 { data: '...', time: '...' }
|
||||
config.data = HiAesUtil.encryptData(plainText, encryptionKey)
|
||||
}
|
||||
|
||||
config.headers = mergeExtraConfig.formatHeader({
|
||||
...this.config.headers,
|
||||
...config.headers,
|
||||
...(mergeExtraConfig.withToken && {
|
||||
[mergeExtraConfig.tokenKey]: mergeExtraConfig.getToken(),
|
||||
}),
|
||||
})
|
||||
config.extraConfig = mergeExtraConfig
|
||||
if (mergeExtraConfig.cancelRepetition) {
|
||||
axiosCanceler.addPending(config)
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
this.errorReport(error?.config.extraConfig.errorLevel, error)
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
this.axiosInstance.interceptors.response.use(
|
||||
(response: ResponseType) => {
|
||||
const { data, config } = response
|
||||
let responseData = response.data.data
|
||||
|
||||
// 假设后端返回格式为 { data: "base64...", time: "..." }
|
||||
if (responseData && responseData.data && responseData.time) {
|
||||
try {
|
||||
const decryptedStr = HiAesUtil.decryptData(
|
||||
responseData.data,
|
||||
responseData.time,
|
||||
encryptionKey,
|
||||
)
|
||||
// 解密后转化为 JSON 对象供后续业务使用
|
||||
responseData = JSON.parse(decryptedStr)
|
||||
} catch (e) {
|
||||
console.error('解密失败:', e)
|
||||
return Promise.reject({ message: '数据解密异常' })
|
||||
}
|
||||
}
|
||||
axiosCanceler.removePending(config)
|
||||
if (data.code == 401) {
|
||||
redirectLogin()
|
||||
return
|
||||
}
|
||||
const resData = config.extraConfig?.originResponseData ? response : responseData
|
||||
if (config.extraConfig?.handleResponse) {
|
||||
if (data.code === 200) {
|
||||
return resData
|
||||
}
|
||||
this.errorReport(
|
||||
config.extraConfig.errorLevel ?? 2,
|
||||
response.data?.msg ?? data?.error ?? '未知错误',
|
||||
)
|
||||
return Promise.reject({
|
||||
...data,
|
||||
message: response.data?.msg ?? data?.error ?? '未知错误',
|
||||
})
|
||||
} else {
|
||||
return resData
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
const status = error?.response?.status
|
||||
const code = error?.code
|
||||
let message = error?.message
|
||||
if (status === 401) {
|
||||
// message = '未登录或登录状态失效'
|
||||
redirectLogin()
|
||||
return
|
||||
}
|
||||
if (code === 'ECONNABORTED') {
|
||||
message = '网络环境太差,请求超时'
|
||||
} else if (code === 'Network Error' || message === 'Network Error') {
|
||||
if (error.response) {
|
||||
message = `${error.response.status}:network连接失败,请求中断`
|
||||
} else {
|
||||
message = '网络好像出现问题了'
|
||||
}
|
||||
}
|
||||
if (error.__CANCEL__) {
|
||||
console.warn('request canceled', error?.message)
|
||||
} else {
|
||||
this.errorReport(error?.config?.extraConfig?.errorLevel, message)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
public get<T>(url: string, params?: Record<string, unknown>, config?: RequestConfig): Promise<T> {
|
||||
return this.axiosInstance.get(url, { ...config, params })
|
||||
}
|
||||
|
||||
public post<D, T>(url: string, data?: D, config?: RequestConfig): Promise<T> {
|
||||
return this.axiosInstance.post(url, data, { ...config })
|
||||
}
|
||||
|
||||
public put<D, T>(url: string, data?: D, config?: RequestConfig): Promise<T> {
|
||||
return this.axiosInstance.put(url, data, { ...config })
|
||||
}
|
||||
|
||||
public patch<D, T>(url: string, data?: D, config?: RequestConfig): Promise<T> {
|
||||
return this.axiosInstance.patch(url, data, { ...config })
|
||||
}
|
||||
|
||||
public delete<D, T>(url: string, params?: D, config?: RequestConfig): Promise<T> {
|
||||
return this.axiosInstance.delete(url, { ...config, params })
|
||||
}
|
||||
}
|
||||
16
src/utils/request/index.ts
Normal file
16
src/utils/request/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import Request from './core'
|
||||
export * from './core'
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_URL
|
||||
|
||||
const request = new Request({
|
||||
baseURL: baseUrl,
|
||||
timeout: 6000,
|
||||
headers: {},
|
||||
extraConfig: {
|
||||
/** 这里是核心配置,一般不需要再去修改request/core.ts */
|
||||
tokenKey: 'Authorization',
|
||||
getToken: () => localStorage.getItem('Authorization') || '',
|
||||
},
|
||||
})
|
||||
|
||||
export default request
|
||||
Loading…
x
Reference in New Issue
Block a user