Files
NixMsg/web/src/api/http.ts
T

86 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { message } from "@/utils/notify";
import type { ApiEnvelope, ApiErrorBody } from "./types";
export class ApiError extends Error {
readonly code: string;
readonly httpStatus: number;
readonly data?: unknown;
constructor(code: string, msg: string, httpStatus = 400, data?: unknown) {
super(msg);
this.name = "ApiError";
this.code = code;
this.httpStatus = httpStatus;
this.data = data;
}
}
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
export interface RequestOptions {
method?: HttpMethod;
body?: unknown;
headers?: Record<string, string>;
/** 为 true 时不弹出错误提示(由调用方处理) */
silent?: boolean;
rawBody?: BodyInit | null;
contentType?: string | null;
}
/**
* 管理接口请求封装:自动带 credentials 与 X-Nixmsg-Request: 1,错误直接 message 显示。
* W4 接真实后端时页面不必改,只需让 admin 模块走本函数。
*/
export async function requestAdmin<T>(path: string, opts: RequestOptions = {}): Promise<T> {
const method = opts.method ?? (opts.body != null || opts.rawBody != null ? "POST" : "GET");
const headers: Record<string, string> = {
"X-Nixmsg-Request": "1",
...opts.headers,
};
let body: BodyInit | undefined | null = opts.rawBody;
if (opts.rawBody === undefined && opts.body !== undefined) {
headers["Content-Type"] = opts.contentType ?? "application/json";
body = JSON.stringify(opts.body);
} else if (opts.contentType) {
headers["Content-Type"] = opts.contentType;
}
const res = await fetch(path, {
method,
credentials: "include",
headers,
body: body ?? undefined,
});
let envelope: ApiEnvelope<T>;
try {
envelope = (await res.json()) as ApiEnvelope<T>;
} catch {
const err = new ApiError("internal", `响应不是 JSON(HTTP ${res.status})`, res.status);
if (!opts.silent) {
message.error(err.message);
}
throw err;
}
if (typeof envelope !== "object" || envelope === null) {
const err = new ApiError("internal", "响应格式无效", res.status);
if (!opts.silent) {
message.error(err.message);
}
throw err;
}
if (envelope.ok) {
return envelope.data;
}
const errBody: ApiErrorBody = envelope.error ?? { code: "internal", message: "未知错误" };
const err = new ApiError(errBody.code, errBody.message, res.status, "data" in envelope ? envelope.data : undefined);
if (!opts.silent) {
message.error(err.message);
}
throw err;
}