| 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(); |