query/src/adapter.ts
2025-06-07 19:07:28 +08:00

106 lines
3.3 KiB
TypeScript

export const methods = ['GET', 'POST'] as const;
export type Method = (typeof methods)[number];
type SimpleObject = Record<string, any>;
export type AdapterOpts = {
url?: string;
headers?: Record<string, string>;
body?: Record<string, any> | FormData; // body 可以是对象、字符串或 FormData
timeout?: number;
method?: Method;
isBlob?: boolean; // 是否返回 Blob 对象, 第一优先
isText?: boolean; // 是否返回文本内容, 第二优先
isPostFile?: boolean; // 是否为文件上传
};
export const isTextForContentType = (contentType: string | null) => {
if (!contentType) return false;
const textTypes = ['text/', 'xml', 'html', 'javascript', 'css', 'csv', 'plain', 'x-www-form-urlencoded'];
return textTypes.some((type) => contentType.includes(type));
};
/**
*
* @param opts
* @param overloadOpts 覆盖fetch的默认配置
* @returns
*/
export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit) => {
const controller = new AbortController();
const signal = controller.signal;
const isBlob = opts.isBlob || false; // 是否返回 Blob 对象
const isText = opts.isText || false; // 是否返回文本内容
const isPostFile = opts.isPostFile || false; // 是否为文件上传
const timeout = opts.timeout || 60000 * 3; // 默认超时时间为 60s * 3
const timer = setTimeout(() => {
controller.abort();
}, timeout);
let method = overloadOpts?.method || opts?.method || 'POST';
let headers = { ...opts?.headers, ...overloadOpts?.headers };
let origin = '';
let url: URL;
if (opts?.url?.startsWith('http')) {
url = new URL(opts.url);
} else {
origin = window?.location?.origin || 'http://localhost:51015';
url = new URL(opts.url, origin);
}
const isGet = method === 'GET';
if (isGet) {
url.search = new URLSearchParams(opts.body as SimpleObject).toString();
}
let body: string | FormData | undefined = undefined;
if (isGet) {
body = undefined;
} else if (isPostFile) {
body = opts.body as FormData; // 如果是文件上传,直接使用 FormData
} else {
headers = {
'Content-Type': 'application/json',
...headers,
};
body = JSON.stringify(opts.body); // 否则将对象转换为 JSON 字符串
}
return fetch(url, {
method: method.toUpperCase(),
signal,
body: body,
...overloadOpts,
headers: headers,
})
.then(async (response) => {
// 获取 Content-Type 头部信息
const contentType = response.headers.get('Content-Type');
if (isBlob) {
return await response.blob(); // 直接返回 Blob 对象
}
const isJson = contentType && contentType.includes('application/json');
// 判断返回的数据类型
if (isJson && !isText) {
return await response.json(); // 解析为 JSON
} else if (isTextForContentType(contentType)) {
return {
code: 200,
status: response.status,
data: await response.text(), // 直接返回文本内容
};
} else {
return response;
}
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('Request timed out and was aborted');
}
console.error(err);
return {
code: 500,
};
})
.finally(() => {
clearTimeout(timer);
});
};
/**
* adapter
*/
export const queryFetch = adapter;