接口调试
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