mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-18 21:29:04 +08:00
🌱 初步创建新请求封装
This commit is contained in:
20
src/enum/app.ts
Normal file
20
src/enum/app.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 应用枚举类
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* 请求方法类型枚举
|
||||
* @since Beta v0.10.0
|
||||
* @see TGApp.App.Response.ReqMethodEnum
|
||||
*/
|
||||
const ReqMethodEnum: typeof TGApp.App.Response.ReqMethod = {
|
||||
GET: "GET",
|
||||
POST: "POST",
|
||||
};
|
||||
|
||||
const appEnum = {
|
||||
req: ReqMethodEnum,
|
||||
};
|
||||
|
||||
export default appEnum;
|
||||
98
src/types/App/Response.d.ts
vendored
Normal file
98
src/types/App/Response.d.ts
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 应用请求封装相关类型定义
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
|
||||
declare namespace TGApp.App.Response {
|
||||
/**
|
||||
* 请求方法枚举
|
||||
* @since Beta v0.10.0
|
||||
* @remarks 只定义了用到的部分
|
||||
*/
|
||||
const ReqMethod = <const>{
|
||||
/** GET */
|
||||
GET: "GET",
|
||||
/** POST */
|
||||
POST: "POST",
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求方法枚举类型
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type ReqMethodEnum = (typeof ReqMethod)[keyof typeof ReqMethod];
|
||||
|
||||
/**
|
||||
* 请求配置
|
||||
* @since Beta v0.10.0
|
||||
* @remarks 只写了用到的部分
|
||||
*/
|
||||
type ReqConf = {
|
||||
/** 请求方法 */
|
||||
method?: ReqMethodEnum;
|
||||
/** 请求头 */
|
||||
headers?: Record<string, string>;
|
||||
/** URL 查询参数 */
|
||||
query?: Record<string, string | number | boolean>;
|
||||
/** 请求体 */
|
||||
body?: string | Record<string, unknown>;
|
||||
/** 是否返回 Blob 数据 */
|
||||
isBlob?: boolean;
|
||||
/** 是否包含 BigInt 数据 */
|
||||
hasBigInt?: boolean;
|
||||
/** 请求超时时间(毫秒) */
|
||||
timeout?: number;
|
||||
/** AbortSignal 用于取消请求 */
|
||||
signal?: AbortSignal;
|
||||
/** 基础 URL */
|
||||
baseURL?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求配置参数
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type ReqConfParams = Omit<ReqConf, "baseURL">;
|
||||
|
||||
/**
|
||||
* 响应类型
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type Resp<T> = {
|
||||
/** 响应数据 */
|
||||
data: T;
|
||||
/** HTTP 状态码 */
|
||||
status: number;
|
||||
/** 状态文本 */
|
||||
statusText: string;
|
||||
/** 响应头 */
|
||||
headers: Headers;
|
||||
/** 原始 Response 对象 */
|
||||
raw: Response;
|
||||
/** 请求配置 */
|
||||
config: ReqConf;
|
||||
};
|
||||
|
||||
/**
|
||||
* HTTP错误响应
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type HttpErr = {
|
||||
/** 错误消息 */
|
||||
message: string;
|
||||
/** HTTP 状态码 */
|
||||
status?: number;
|
||||
/** 状态文本 */
|
||||
statusText?: string;
|
||||
/** 响应数据 */
|
||||
data?: unknown;
|
||||
/** 原始错误 */
|
||||
cause?: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* Http错误构建参数
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
type HttpErrParams = Omit<HttpErr, "message">;
|
||||
}
|
||||
216
src/utils/TGHttps.ts
Normal file
216
src/utils/TGHttps.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 应用请求客户端封装
|
||||
* @since Beta v0.10.0
|
||||
*/
|
||||
|
||||
import { type ClientOptions, fetch } from "@tauri-apps/plugin-http";
|
||||
import JSONBig from "json-bigint";
|
||||
|
||||
import TGLogger from "./TGLogger.js";
|
||||
|
||||
/**
|
||||
* 构建 URL 查询字符串
|
||||
* @since Beta v0.10.0
|
||||
* @param params - 查询参数
|
||||
* @returns 查询字符串
|
||||
*/
|
||||
function buildQueryString(params: Record<string, string | number | boolean>): string {
|
||||
const searchParams = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
searchParams.append(key, String(value));
|
||||
}
|
||||
return searchParams.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 HTTP 错误对象
|
||||
* @since Beta v0.10.0
|
||||
* @param message - 错误消息
|
||||
* @param options - 错误选项
|
||||
* @returns HTTP 错误对象
|
||||
*/
|
||||
function createHttpError(
|
||||
message: string,
|
||||
options: TGApp.App.Response.HttpErrParams,
|
||||
): TGApp.App.Response.HttpErr {
|
||||
return {
|
||||
message,
|
||||
status: options.status,
|
||||
statusText: options.statusText,
|
||||
data: options.data,
|
||||
cause: options.cause,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析响应数据
|
||||
* @since Beta v0.10.0
|
||||
* @param response - 原始响应
|
||||
* @param config - 请求配置
|
||||
* @returns 解析后的数据
|
||||
*/
|
||||
async function parseResponse<T>(
|
||||
response: Response,
|
||||
config: TGApp.App.Response.ReqConf,
|
||||
): Promise<T> {
|
||||
if (config.isBlob) {
|
||||
return <T>await response.arrayBuffer();
|
||||
}
|
||||
if (config.hasBigInt) {
|
||||
return <T>JSONBig.parse(await response.text());
|
||||
}
|
||||
return <T>await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 HTTP 请求
|
||||
* @since Beta v0.10.0
|
||||
* @param method - 请求方法
|
||||
* @param url - 请求地址
|
||||
* @param config - 请求配置
|
||||
* @returns 响应对象
|
||||
*/
|
||||
async function request<T>(
|
||||
method: TGApp.App.Response.ReqMethodEnum,
|
||||
url: string,
|
||||
config: TGApp.App.Response.ReqConf = {},
|
||||
): Promise<TGApp.App.Response.Resp<T>> {
|
||||
const timeout = config.timeout ?? 30000;
|
||||
|
||||
// 构建完整 URL
|
||||
let finalUrl = url;
|
||||
if (config.query) {
|
||||
const queryString = buildQueryString(config.query);
|
||||
if (queryString) {
|
||||
finalUrl += `?${queryString}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
const httpHeaders = new Headers();
|
||||
if (config.headers) {
|
||||
for (const [key, value] of Object.entries(config.headers)) {
|
||||
httpHeaders.append(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建请求选项
|
||||
const fetchOptions: RequestInit & ClientOptions = {
|
||||
method,
|
||||
headers: httpHeaders,
|
||||
};
|
||||
|
||||
// 添加请求体
|
||||
if (config.body !== undefined) {
|
||||
fetchOptions.body = typeof config.body === "string" ? config.body : JSON.stringify(config.body);
|
||||
}
|
||||
|
||||
// 调试日志
|
||||
if (config.isBlob) {
|
||||
console.debug(`[TGHttps] Fetch Blob: ${finalUrl}`);
|
||||
} else {
|
||||
console.debug(`[TGHttps] ${method} ${finalUrl}`);
|
||||
}
|
||||
|
||||
// 创建超时控制器
|
||||
const timeoutController = new AbortController();
|
||||
const timeoutId = setTimeout(() => timeoutController.abort(), timeout);
|
||||
|
||||
// 合并 AbortSignal
|
||||
let combinedSignal: AbortSignal;
|
||||
if (config.signal) {
|
||||
combinedSignal = AbortSignal.any([config.signal, timeoutController.signal]);
|
||||
} else {
|
||||
combinedSignal = timeoutController.signal;
|
||||
}
|
||||
fetchOptions.signal = combinedSignal;
|
||||
|
||||
try {
|
||||
const rawResponse = await fetch(finalUrl, fetchOptions);
|
||||
|
||||
// 清除超时定时器
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// 检查 HTTP 状态
|
||||
if (!rawResponse.ok) {
|
||||
const errorText = await rawResponse.text().catch(() => "Unknown error");
|
||||
throw createHttpError(`HTTP Error: ${rawResponse.status} ${rawResponse.statusText}`, {
|
||||
status: rawResponse.status,
|
||||
statusText: rawResponse.statusText,
|
||||
data: errorText,
|
||||
});
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
const data = await parseResponse<T>(rawResponse, config);
|
||||
|
||||
return {
|
||||
data: data,
|
||||
status: rawResponse.status,
|
||||
statusText: rawResponse.statusText,
|
||||
headers: rawResponse.headers,
|
||||
raw: rawResponse,
|
||||
config: config,
|
||||
};
|
||||
} catch (error) {
|
||||
// 清除超时定时器
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
let httpError: TGApp.App.Response.HttpErr;
|
||||
|
||||
if (typeof error === "object" && error !== null && "message" in error) {
|
||||
httpError = <TGApp.App.Response.HttpErr>error;
|
||||
} else if (error instanceof Error) {
|
||||
httpError = createHttpError(error.message, { cause: error });
|
||||
} else {
|
||||
httpError = createHttpError(String(error), { cause: error });
|
||||
}
|
||||
|
||||
// 记录错误日志
|
||||
await TGLogger.Error(`[TGHttps] Request failed: ${httpError.message}`);
|
||||
|
||||
throw httpError;
|
||||
}
|
||||
}
|
||||
|
||||
const TGHttps = {
|
||||
/**
|
||||
* GET 请求
|
||||
* @since Beta v0.10.0
|
||||
* @param url - 请求地址
|
||||
* @param config - 请求配置
|
||||
* @returns 响应对象
|
||||
*/
|
||||
get: <T>(
|
||||
url: string,
|
||||
config?: TGApp.App.Response.ReqConfParams,
|
||||
): Promise<TGApp.App.Response.Resp<T>> => request<T>("GET", url, config),
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
* @since Beta v0.10.0
|
||||
* @param url - 请求地址
|
||||
* @param config - 请求配置
|
||||
* @returns 响应对象
|
||||
*/
|
||||
post: <T>(
|
||||
url: string,
|
||||
config?: TGApp.App.Response.ReqConfParams,
|
||||
): Promise<TGApp.App.Response.Resp<T>> => request<T>("POST", url, config),
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
* @since Beta v0.10.0
|
||||
* @param method - 请求方法
|
||||
* @param url - 请求地址
|
||||
* @param config - 请求配置
|
||||
* @returns 响应对象
|
||||
*/
|
||||
request: <T>(
|
||||
method: TGApp.App.Response.ReqMethodEnum,
|
||||
url: string,
|
||||
config?: TGApp.App.Response.ReqConfParams,
|
||||
): Promise<TGApp.App.Response.Resp<T>> => request<T>(method, url, config),
|
||||
};
|
||||
|
||||
export default TGHttps;
|
||||
Reference in New Issue
Block a user