/**
* 拦截器使用方式 拦截器是全局拦截
* import request, { interceptors } from '@/utils/request.uts'
* import { VITE_APP_TABBAR_HOME_PAGE } from '@/config/page-setting.uts'
* // 添加请求拦截器(示例:统一加版本号header)
interceptors.useRequest!(async (config) => {
config.requestOpts.header = config.requestOpts.header ?? {}
config.requestOpts.header['X-App-Version'] = '1.0.0'
return config
})
// 添加响应拦截器(示例:处理token过期)
interceptors.useResponse!(
async (response) => {
// token过期逻辑
response.code === 60030 && uni.switchTab({ url: VITE_APP_TABBAR_HOME_PAGE })
return response
},
async (error) => {
// 统一打印错误日志
console.error('请求异常:', error)
return Promise.reject(error)
}
)
封装请求示例:
export function test(data:any)
{
return request<any>({
url: '/test',
method: 'GET',
data,
},{uniCloud:false})
}
*/
import { ERROR_PATH } from '@/config/image-setting.uts'
import { VITE_APP_BASE_API } from '@/config/setting.uts'
import { userState } from '@/store/user-store.uts'
import type {ApiResponse,RequestExtra} from '@/api/youhujun/request/api-type.uts'
// 请求拦截器配置类型
type InterceptorRequestConfig = {
requestOpts: RequestOptions<ApiResponse>;
extra: RequestExtra;
}
// 请求拦截器类型(统一返回Promise)
type RequestInterceptor = (config: InterceptorRequestConfig) => Promise<InterceptorRequestConfig>
// 响应拦截器类型
type ResponseInterceptor = (response: ApiResponse) => Promise<ApiResponse>
// 错误拦截器类型
type ErrorInterceptor = (error: any) => Promise<any>
// ====================== 拦截器管理器 ======================
const requestInterceptors: RequestInterceptor[] = []
const responseInterceptors: ResponseInterceptor[] = []
const responseErrorInterceptors: ErrorInterceptor[] = []
const interceptors = {
// 非空函数兜底:()=>void 确保编译器认为方法不可能为null
useRequest: ((interceptor: RequestInterceptor) => {
requestInterceptors.push(interceptor)
}) as (interceptor: RequestInterceptor) => void,
useResponse: (((onFulfilled: ResponseInterceptor, onRejected?: ErrorInterceptor) => {
responseInterceptors.push(onFulfilled)
onRejected && responseErrorInterceptors.push(onRejected)
}) as (onFulfilled: ResponseInterceptor, onRejected?: ErrorInterceptor) => void),
clear: (((type: 'request' | 'response' | 'responseError') => {
type === 'request' && (requestInterceptors.length = 0)
type === 'response' && (responseInterceptors.length = 0)
type === 'responseError' && (responseErrorInterceptors.length = 0)
}) as (type: 'request' | 'response' | 'responseError') => void)
}
const isUTSJSONObject = (obj: any): obj is UTSJSONObject => {
if (obj === null || typeof obj !== 'object') return false;
// 匹配特征:构造函数名含"UTSJSONObject" + 存在它的专属方法(如resolveKeyPath)
return (
obj.constructor.name.includes('UTSJSONObject') && // 覆盖UTSJSONObject/UTSJSONObject2
(typeof obj.resolveKeyPath === 'function' || // 环境里有这个方法
typeof obj.keys === 'function' ||
typeof obj.get === 'function')
);
};
// 递归处理嵌套数据,将所有UTSJSONObject转为普通JS对象
const recursiveHandleUTSObject = (data: any): any => {
// 1. 如果是UTSJSONObject + data非空 + keys是函数
if (isUTSJSONObject(data) && data !== null) {
const plainObj: any = {};
const innerMethods = ['resolveKeyPath', '_g', '_f', 'keys', 'get', 'toMap', '_getValue'];
for (const key in data) {
if (data.hasOwnProperty(key) && !innerMethods.includes(key)) {
//console.log('遍历的业务key:', key);
// 递归处理子对象(比如data对应的UTSJSONObject2)
plainObj[key] = recursiveHandleUTSObject(data[key]);
}
}
//console.log('当前层级转换结果:', plainObj); // 验证子对象是否转成普通对象
return plainObj;
}
// 2. 处理数组(原有逻辑不变)
if (Array.isArray(data)) {
return data.map(item => recursiveHandleUTSObject(item));
}
// 3. 处理普通对象(原有逻辑不变)
if (data !== null && typeof data === 'object' && !isUTSJSONObject(data)) {
const plainObj: any = {};
for (const key in data) {
if (data.hasOwnProperty(key)) {
plainObj[key] = recursiveHandleUTSObject(data[key]);
}
}
return plainObj;
}
// 4. 基本类型,直接返回(原有逻辑不变)
return data;
}
// 统一转换为自定义类型(通用方法,仅修改内部逻辑)
const convertToCustomType = <T>(data: any): T | null => {
try {
//console.log('转换前类型:', data.constructor.name, '是否为UTSJSONObject:', isUTSJSONObject(data));
const processedData = recursiveHandleUTSObject(data);
//console.log('转换后实际类型(普通对象):', processedData); // 直接打印对象,看属性
// 去掉JSON序列化,直接返回处理后的普通对象(已无UTS特性)
return processedData as T;
} catch (e) {
console.error("数据转换失败:", e);
return null;
}
};
// ====================== 核心请求方法 ======================
const service = async<T = ApiResponse>(
requestOpts: RequestOptions<ApiResponse>,
extra: RequestExtra = { uniCloud: false }
) => {
// 初始化拦截器配置
let config: InterceptorRequestConfig = {
requestOpts: { ...requestOpts },
extra: { ...extra }
}
// 执行所有请求拦截器(按顺序)
for (const interceptor of requestInterceptors) {
config = await interceptor(config)
}
const finalRequestOpts = config.requestOpts
const finalExtra = config.extra
// 1. URL必填校验(无try/catch,直接判断+reject)
if (!finalRequestOpts.url || finalRequestOpts.url.trim() === '') {
uni.showToast({
image: ERROR_PATH,
title: '请求URL不能为空',
mask: true,
duration: 2000
})
return Promise.reject(new Error('请求URL不能为空'))
}
// 2. 提取核心字段
let finalUrl = finalRequestOpts.url.trim()
const rawMethod = finalRequestOpts.method || 'GET'
const finalMethod = rawMethod.toUpperCase() as RequestMethod
const finalData = finalRequestOpts.data || {}
const finalHeader = finalRequestOpts.header ?? {}
const { uniCloud = false } = finalExtra
// 3. uniCloud逻辑
if (!uniCloud) {
userState.token && (finalHeader['X-Token'] = userState.token)
!/^https?:\/\//.test(finalUrl) && (finalUrl = VITE_APP_BASE_API + finalUrl)
}
// 4. 发起请求(返回Promise)
return new Promise<T | null>((resolve, reject) => {
uni.request<ApiResponse>({
url: finalUrl,
method: finalMethod,
data: finalData,
header: finalHeader,
timeout: finalRequestOpts.timeout || 60000,
success: async (res: RequestSuccess<ApiResponse>) => {
// 响应数据为空 → 直接reject
if (!res.data) {
uni.showToast({ image: ERROR_PATH, title: '请求数据异常', mask: true, duration: 2000 })
return reject(new Error('请求数据异常'))
}
// 状态码非200 → 直接reject
if (res.statusCode !== 200) {
uni.showToast({ image: ERROR_PATH, title: `请求失败(${res.statusCode})`, mask: true, duration: 2000 })
return reject(new Error(`请求失败(${res.statusCode})`))
}
// 执行响应成功拦截器
let responseData = res.data
for (const interceptor of responseInterceptors) {
responseData = await interceptor(responseData)
}
// 统一转换为自定义类型(核心步骤)
const finalResponse = convertToCustomType<T>(responseData);
// 业务码成功 → resolve
if (responseData.code === 0) {
return resolve(finalResponse)
}
// 业务码失败 → 执行错误拦截器后reject
uni.showToast({ image: ERROR_PATH, title: responseData.msg || '请求失败', mask: true, duration: 2000 })
let error = finalResponse;
for (const interceptor of responseErrorInterceptors) {
error = await interceptor(error)
}
reject(error)
},
fail: async (err: RequestFail) => {
// 请求失败 → 执行错误拦截器后reject
uni.showToast({ image: ERROR_PATH, title: '网络异常,请重试', mask: true, duration: 2000 })
let error = err
for (const interceptor of responseErrorInterceptors) {
error = await interceptor(error)
}
reject(error)
}
})
})
}
// 导出:拆分导出(无Object.assign,Android兼容)
export { service as default, interceptors }