接口调试

This commit is contained in:
speakeloudest 2025-12-30 01:25:17 -08:00
parent de6dfb5e20
commit ced65527d5
3 changed files with 282 additions and 0 deletions

View 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
View 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-warning2-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 })
}
}

View 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