2024-01-18 17:04:07 +08:00

192 lines
5.4 KiB
JavaScript

import axios from 'axios';
import { ApplyPluginsType, plugin } from '@fesjs/fes';
import { ref } from 'vue';
import scheduler from './scheduler';
import { checkHttpRequestHasBody, isFunction } from './helpers';
import paramsProcess from './paramsProcess';
import genRequestKey from './genRequestKey';
import preventRepeatReq from './preventRepeatReq';
import cacheControl from './cacheControl';
function addInterceptors(instance, interceptors, type = 'request') {
interceptors.forEach((fn) => {
if (Array.isArray(fn)) {
instance.interceptors[type].use(...fn);
} else if (isFunction(fn)) {
instance.interceptors[type].use(fn);
}
});
}
function addRequestInterceptors(instance, interceptors) {
addInterceptors(instance, interceptors, 'request');
}
function addResponseInterceptors(instance, interceptors) {
addInterceptors(instance, interceptors, 'response');
}
async function axiosMiddleware(context, next) {
try {
context.response = await context.instance.request(context.config);
} catch (error) {
context.error = error;
}
await next();
}
function getRequestInstance() {
const {
dataHandler,
errorHandler,
requestInterceptors = [],
responseInterceptors = [],
...otherConfigs
} = plugin.applyPlugins({
key: 'request',
type: ApplyPluginsType.modify,
initialValue: {},
});
const defaultConfig = Object.assign(
{
timeout: 10000,
withCredentials: true,
},
otherConfigs,
);
const instance = axios.create(defaultConfig);
addRequestInterceptors(instance, requestInterceptors);
addResponseInterceptors(instance, responseInterceptors);
// 洋葱模型内部应该这是对数据的处理,避免有副作用调用
scheduler.use(paramsProcess).use(genRequestKey).use(cacheControl).use(preventRepeatReq).use(axiosMiddleware);
return {
context: {
errorHandler,
dataHandler: dataHandler || ((data) => data),
instance,
defaultConfig,
},
request: scheduler.compose(),
};
}
function userConfigHandler(url, data, options = {}) {
options.url = url;
options.method = (options.method || 'post').toUpperCase();
if (checkHttpRequestHasBody(options.method)) {
options.data = data;
} else {
options.params = data;
}
return options;
}
let currentRequestInstance = null;
function createContext(userConfig) {
return {
...currentRequestInstance.context,
config: {
...currentRequestInstance.context.defaultConfig,
...userConfig,
},
};
}
function getCustomerHandler(ctx, options = {}) {
const { dataHandler, errorHandler } = ctx;
return {
dataHandler: options.dataHandler || dataHandler,
errorHandler: options.errorHandler || errorHandler,
};
}
function formatOptions(options = {}) {
if (typeof options === 'string') {
options = {
method: options,
};
}
return options;
}
function getReqInstance() {
if (!currentRequestInstance) {
currentRequestInstance = getRequestInstance();
}
return currentRequestInstance;
}
const _request = (url, data, options, onSuccess) => {
options = formatOptions(options);
const reqInstance = getReqInstance();
const userConfig = userConfigHandler(url, data, options);
const context = createContext(userConfig);
const { dataHandler, errorHandler } = getCustomerHandler(context, options);
return reqInstance.request(context).then(async () => {
if (context.config.skipErrorHandler) {
console.warn('3.x 已移除 skipErrorHandler 参数,请改用 dataHandler 处理');
if (
((context.error || context.response?.data?.code !== '0') && context.config.skipErrorHandler === true) ||
context.response?.data?.code === context.config.skipErrorHandler
) {
return Promise.reject(context.response);
}
}
if (!context.error) {
if (onSuccess) {
return onSuccess(dataHandler(context.response.data, context.response), context);
}
return dataHandler(context.response.data, context.response);
}
errorHandler && errorHandler(context.error);
return Promise.reject(context.error);
});
};
export const request = _request;
export const rawRequest = (url, data, options) =>
_request(url, data, options, (d, ctx) => {
ctx.response.rawData = ctx.response.data;
ctx.response.data = d;
return ctx.response;
});
function isPromiseLike(obj) {
return !!obj && typeof obj === 'object' && typeof obj.then === 'function';
}
export const useRequest = (url, data, options = {}) => {
const loadingRef = ref(true);
const errorRef = ref(null);
const dataRef = ref(null);
let promise;
if (isPromiseLike(url)) {
promise = url;
} else {
promise = request(url, data, options);
}
promise
.then((res) => {
dataRef.value = res;
})
.catch((error) => {
errorRef.value = error;
})
.finally(() => {
loadingRef.value = false;
});
return {
loading: loadingRef,
error: errorRef,
data: dataRef,
};
};