统一盲盒前端 API 调用层并收敛错误处理

This commit is contained in:
2026-03-03 13:17:42 +08:00
parent 532d8ff7ad
commit 22610f0b59
7 changed files with 204 additions and 128 deletions
+83
View File
@@ -12,6 +12,89 @@ export class FetchError extends Error {
}
}
export class ApiRequestError extends Error {
constructor(
message: string,
public status: number,
public payload?: unknown,
) {
super(message);
this.name = "ApiRequestError";
}
}
function extractErrorMessage(payload: unknown): string | null {
if (
payload &&
typeof payload === "object" &&
"error" in payload &&
typeof payload.error === "string"
) {
return payload.error;
}
if (typeof payload === "string" && payload.trim()) {
return payload;
}
return null;
}
async function parsePayload(res: Response): Promise<unknown> {
if (res.status === 204) return undefined;
if (typeof res.text === "function") {
const text = await res.text();
if (!text) return undefined;
try {
return JSON.parse(text);
} catch {
return text;
}
}
if (typeof res.json === "function") {
try {
return await res.json();
} catch {
return undefined;
}
}
return undefined;
}
type RequestJsonInit<TBody> = Omit<RequestInit, "body"> & {
body?: TBody;
};
/**
* Shared JSON requester for imperative client-side API calls.
* - Parses JSON/text payloads automatically
* - Throws ApiRequestError with normalized message on non-2xx
*/
export async function requestJson<TResponse = unknown, TBody = unknown>(
url: string,
init: RequestJsonInit<TBody> = {},
): Promise<TResponse> {
const { body, headers, ...rest } = init;
const hasBody = body !== undefined;
const mergedHeaders = new Headers(headers);
if (hasBody && !mergedHeaders.has("Content-Type")) {
mergedHeaders.set("Content-Type", "application/json");
}
const res = await fetch(url, {
...rest,
headers: mergedHeaders,
body: hasBody ? JSON.stringify(body) : undefined,
});
const payload = await parsePayload(res);
if (!res.ok) {
const message = extractErrorMessage(payload) ?? "请求失败";
throw new ApiRequestError(message, res.status, payload);
}
return payload as TResponse;
}
export async function fetcher<T = unknown>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) {