message / frontend /src /api /client.ts
hunian
chore(docs): 更新 README 以支持 React SPA 和 uv 管理
d0c18f0
Raw
History Blame Contribute Delete
3.09 kB
import { ApiError } from '@typings/api';
const BASE_URL = '/api';
const DEFAULT_TIMEOUT = 30000;
interface RequestOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
body?: any;
headers?: Record<string, string>;
timeout?: number;
}
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string = BASE_URL) {
this.baseUrl = baseUrl;
}
private async handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
let errorMessage = '请求失败';
if (response.status === 401) {
errorMessage = '未授权,请重新登录';
} else if (response.status === 403) {
errorMessage = '没有权限执行此操作';
} else if (response.status === 404) {
errorMessage = '请求的资源不存在';
} else if (response.status >= 500) {
errorMessage = '服务器错误,请稍后重试';
} else {
try {
const errorData = await response.json();
errorMessage = errorData.detail || errorData.message || errorMessage;
} catch {
// 无法解析错误详情
}
}
throw new ApiError(response.status, errorMessage);
}
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
return response.json();
}
return {} as T;
}
async request<T>(path: string, options: RequestOptions = {}): Promise<T> {
const { method = 'GET', body, headers = {}, timeout = DEFAULT_TIMEOUT } = options;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const config: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
signal: controller.signal,
};
if (body && method !== 'GET') {
config.body = JSON.stringify(body);
}
try {
const response = await fetch(`${this.baseUrl}${path}`, config);
clearTimeout(timeoutId);
return this.handleResponse<T>(response);
} catch (error: any) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new ApiError(0, '请求超时,请检查网络连接');
}
if (!error.status) {
throw new ApiError(0, '网络错误,请检查网络连接');
}
throw error;
}
}
async get<T>(path: string): Promise<T> {
return this.request<T>(path);
}
async post<T>(path: string, body?: any): Promise<T> {
return this.request<T>(path, { method: 'POST', body });
}
async put<T>(path: string, body?: any): Promise<T> {
return this.request<T>(path, { method: 'PUT', body });
}
async delete<T>(path: string): Promise<T> {
return this.request<T>(path, { method: 'DELETE' });
}
async upload<T>(path: string, formData: FormData): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`, {
method: 'POST',
body: formData,
});
return this.handleResponse<T>(response);
}
}
export const apiClient = new ApiClient();