Industrial-image-management.../src/utils/http.ts

228 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import axios from 'axios'
import qs from 'query-string'
import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { useUserStore } from '@/stores'
import { getToken } from '@/utils/auth'
import modalErrorWrapper from '@/utils/modal-error-wrapper'
import messageErrorWrapper from '@/utils/message-error-wrapper'
import notificationErrorWrapper from '@/utils/notification-error-wrapper'
import router from '@/router'
interface ICodeMessage {
[propName: number]: string
}
const StatusCodeMessage: ICodeMessage = {
200: '服务器成功返回请求的数据',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)',
204: '删除数据成功',
400: '请求错误(400)',
401: '未授权,请重新登录(401)',
403: '拒绝访问(403)',
404: '请求出错(404)',
408: '请求超时(408)',
500: '服务器错误(500)',
501: '服务未实现(501)',
502: '网络错误(502)',
503: '服务不可用(503)',
504: '网络超时(504)',
}
const http: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_PREFIX ?? import.meta.env.VITE_API_BASE_URL,
timeout: 30 * 1000,
})
const handleError = (msg: string) => {
if (msg.length >= 15) {
return notificationErrorWrapper({
content: msg || '服务器端错误',
duration: 5 * 1000,
})
}
return messageErrorWrapper({
content: msg || '服务器端错误',
duration: 5 * 1000,
})
}
// 请求拦截器
http.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = getToken()
if (token) {
if (!config.headers) {
config.headers = {}
}
config.headers.Authorization = `${token}`
}
return config
},
(error) => Promise.reject(error),
)
// 响应拦截器
http.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response
// 兼容返回数据中使用rows字段的情况
if (data && data.rows !== undefined && data.data === undefined) {
data.data = data.rows
}
// 兼容不同的API响应结构
const { success, code, msg } = data
// 检查响应类型是否是blob
if (response.request.responseType === 'blob') {
const contentType = data.type
if (contentType.startsWith('application/json')) {
const reader = new FileReader()
reader.readAsText(data)
reader.onload = () => {
try {
const result = JSON.parse(reader.result as string)
const isSuccess = result.success !== undefined ? result.success : (result.code === 200 || result.code === '200')
if (!isSuccess) {
handleError(result.msg || '操作失败')
}
} catch (error) {
handleError('解析响应数据失败')
}
}
return Promise.reject(msg)
} else {
return response
}
}
// 判断请求是否成功明确的success字段为true或者code为200都视为成功
const isSuccess = success !== undefined ? success : (code === 200 || code === '200')
if (isSuccess) {
return response
}
// Token 失效
if (code === '401' && response.config.url !== '/auth/user/info') {
modalErrorWrapper({
title: '提示',
content: msg,
maskClosable: false,
escToClose: false,
okText: '重新登录',
async onOk() {
const userStore = useUserStore()
await userStore.logoutCallBack()
await router.replace('/login')
},
})
} else {
handleError(msg)
}
return Promise.reject(new Error(msg || '服务器端错误'))
},
(error: AxiosError) => {
if (!error.response) {
handleError('网络连接失败,请检查您的网络')
return Promise.reject(error)
}
const status = error.response?.status
const errorMsg = StatusCodeMessage[status] || '服务器暂时未响应,请刷新页面并重试。若无法解决,请联系管理员'
handleError(errorMsg)
return Promise.reject(error)
},
)
const request = async <T = unknown>(config: AxiosRequestConfig): Promise<ApiRes<T>> => {
return http.request<T>(config)
.then((res: AxiosResponse) => {
// 处理返回数据结构兼容rows和data字段
const responseData = res.data
// 如果返回的数据中有rows字段但没有data字段将rows赋值给data
if (responseData.rows !== undefined && responseData.data === undefined) {
responseData.data = responseData.rows
}
// 如果返回的code是200但没有设置success字段将success设置为true
if ((responseData.code === 200 || responseData.code === '200') && responseData.success === undefined) {
responseData.success = true
}
return responseData
})
.catch((err: { msg: string }) => Promise.reject(err))
}
const requestNative = async <T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse> => {
return http.request<T>(config)
.then((res: AxiosResponse) => res)
.catch((err: { msg: string }) => Promise.reject(err))
}
// 完全绕过拦截器的请求方法
const requestRaw = async <T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse> => {
// 创建一个新的axios实例不包含拦截器
const rawAxios = axios.create({
baseURL: import.meta.env.VITE_API_PREFIX ?? import.meta.env.VITE_API_BASE_URL,
timeout: 30 * 1000,
})
// 只添加请求拦截器来设置token不添加响应拦截器
rawAxios.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = getToken()
if (token) {
if (!config.headers) {
config.headers = {}
}
config.headers.Authorization = `${token}`
}
return config
},
(error) => Promise.reject(error),
)
return rawAxios.request<T>(config)
}
const createRequest = (method: string) => {
return <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
return request({
method,
url,
[method === 'get' ? 'params' : 'data']: params,
...(method === 'get'
? {
paramsSerializer: (obj) => qs.stringify(obj),
}
: {}),
...config,
})
}
}
const download = (url: string, params?: object, config?: AxiosRequestConfig): Promise<AxiosResponse> => {
return requestNative({
method: 'get',
url,
responseType: 'blob',
params,
paramsSerializer: (obj) => qs.stringify(obj),
...config,
})
}
export default {
get: createRequest('get'),
post: createRequest('post'),
put: createRequest('put'),
patch: createRequest('patch'),
del: createRequest('delete'),
request,
requestNative,
requestRaw,
download,
}