njzscloud-dispose-web/src/common/utils/http-util.ts

262 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { serverBaseUrl } from '@/common'
import axios, {
type AxiosRequestConfig,
type AxiosResponse,
} from 'axios'
import * as qs from 'qs'
import { useAppUserStore } from '@/common/app/app-user-store.ts'
import Utils from '@/common/utils/index.ts'
import mime from '@/common/utils/mime.ts'
import Evt from '@/common/utils/evt.ts'
import { ElMessage } from 'element-plus'
/**
* HTTP 统一响应结构
*/
export interface R<T = any> {
code: number;
msg: string;
message: any;
success: boolean;
data: T;
headers?: AxiosRequestConfig['headers']
}
type AxiosConfig = Pick<AxiosRequestConfig, 'headers' | 'params' | 'responseType'>
// const closeUrls = closeUrl.split(',')
// type ParamsSerializerType = Extract<AxiosRequestConfig['paramsSerializer'], any>;
/**
* Query 参数处理器
*
* @param params 参数内容
*/
const paramsSerializer = (params: any) => {
return qs.stringify(params, {indices: false, allowDots: true})
}
/**
* 统一错误处理函数
*/
/* function errHandler(r?: R) {
Toast.error(r?.message ?? '操作失败')
} */
const errHandler = Utils.throttle(500, (r?: AxiosResponse<R<any>, any>) => {
console.log('异常处理', r)
ElMessage.error(r?.data?.message ?? '服务器错误')
})
/**
* axios 实例
*/
const httpUtil = axios.create({
timeout: 10000,
baseURL: serverBaseUrl,
headers: {
Accept: mime.JSON,
},
})
/**
* 配置请求拦截器
*/
httpUtil.interceptors.request.use(
config => {
const appUserStore = useAppUserStore()
config.headers.Authorization = appUserStore.token
config.formSerializer = {indexes: null}
return config
},
error => {
// TODO 请求失败日志
console.error('HTTP 请求发送失败', error)
return Promise.reject(error)
},
)
/**
* 配置响应拦截器
*/
httpUtil.interceptors.response.use(
response => {
// console.log('HTTP 请求结果', response.config.url, response)
// vite 代理失败时 响应码为 200 响应内容为空
if (response.config.responseType !== 'json') {
return Promise.resolve(response)
}
if (response.data == null) {
response.data = {code: 0, success: true, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers}
}
response.data.headers = response.headers
if (response.data.code === 0) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
error => {
console.error('HTTP 请求失败', error)
if (error.response != null && error.response.status === 403) {
Evt.emit('logout')
}
if (error.response != null) {
error.response.data = {...error.response.data, headers: error.response.headers}
} else if (error.request != null) {
error.response = {
data: {code: 9999, success: false, msg: '网络异常', message: '网络异常', data: null},
}
} else {
error.response = {
data: {code: 5555, success: false, msg: '请求发送失败', message: '请求发送失败', data: null},
}
}
return Promise.reject(error.response)
},
)
/**
* GET 请求JSON
*
* @param url 请求地址
* @param params Query 参数
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function get<T = any>(url: string, params?: any, disposeErr: boolean = true) {
/* if (closeUrls.includes(url)) {
return Promise.reject({code: 0, success: true, msg: '', message: '', data: null} as R<T>)
} */
return httpUtil.get<R<T>>(url, {params, paramsSerializer, responseType: 'json'})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res as T)
})
}
/**
* POST 请求JSON
*
* @param url 请求地址
* @param body Body 参数
* @param params 参数
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function post<T>(url: string, body?: any, params?: any, disposeErr: boolean = true) {
/* if (closeUrls.includes(url)) {
return Promise.reject({code: 0, success: true, msg: '', message: '', data: null} as R<T>)
} */
return httpUtil.post<R<T>>(url, body, {responseType: 'json', params})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
/**
* POST 请求(编码表单)
*
* @param url 请求地址
* @param body Body 参数
* @param config Axios 配置
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function postForm<T>(url: string, body: any, config?: AxiosConfig, disposeErr: boolean = true) {
return httpUtil.postForm<R<T>>(url, paramsSerializer(body),
{
headers: {
...(config?.headers ?? {}),
'Content-Type': mime.FORM,
},
params: config?.params,
responseType: config?.responseType ?? 'json',
})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
/**
* POST 请求(多部分表单)
*
* @param url 请求地址
* @param body Body 参数
* @param config Axios 配置
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function postMltForm<T>(url: string, body: any, config?: AxiosConfig, disposeErr: boolean = true) {
return httpUtil.postForm<R<T>>(url, body,
{
headers: {
...(config?.headers ?? {}),
'Content-Type': mime.MLT_FORM,
},
params: config?.params,
responseType: config?.responseType ?? 'json',
})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
function getFileName(contentDisposition: string) {
// 检查content-disposition是否存在
if (!contentDisposition) {
return null
}
// 查找filename=部分
const match = contentDisposition.match(/filename=(.+)/)
if (!match || match.length < 2) {
return null
}
// 提取并解码文件名
const fileNameEncoded = match[1].trim()
// 移除可能存在的引号
const fileNameWithoutQuotes = fileNameEncoded.replace(/["']/g, '')
// 解码URL编码的字符串
return decodeURIComponent(fileNameWithoutQuotes)
}
export function download(url: string, data?: any, params?: any, defaultName: string = '下载的文件', disposeErr: boolean = true) {
return httpUtil.post(url, data, {params, paramsSerializer, responseType: 'arraybuffer'})
.then(res => {
const data = res.data
if (!data || data.byteLength <= 0) {
// 错误提示
return Promise.reject({code: 9999, success: false, msg: '文件获取失败', message: '文件获取失败', data: null, headers: res.headers})
}
const contentDisposition = res.headers['Content-Disposition'] ?? res.headers['content-disposition']
const filename = getFileName(contentDisposition) ?? defaultName
// 将二进制流转为blob
const blob = new Blob([ data ])
return Promise.resolve({code: 0, success: true, msg: '成功', message: '文件获取成功', data: {data: blob, filename}, headers: res.headers})
})
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
export function getFileUrl(path?: string) {
if (path == null || path.length <= 0) {
return ''
}
const appUserStore = useAppUserStore()
return serverBaseUrl + path + '?authorization=' + appUserStore.token
}
export default {
get,
post,
postForm,
postMltForm,
download,
getFileUrl,
ins: httpUtil,
}