File size: 2,928 Bytes
40f23a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// 基础API请求封装
class ApiClient {
  constructor(baseURL = '', options = {}) {
    this.baseURL = baseURL
    this.timeout = options.timeout || 30000 // 改为30秒
    this.headers = {
      // 不要默认设置Content-Type,避免触发预检请求
      ...options.headers
    }
  }

  // 基础请求方法
  async request(url, options = {}) {
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), this.timeout)

    try {
      const fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`
      
      // 对于所有请求,都不要添加默认的Content-Type头部,避免触发预检请求
      const fetchOptions = {
        signal: controller.signal,
        ...options
      }

      const response = await fetch(fullUrl, fetchOptions)

      clearTimeout(timeoutId)

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }

      const contentType = response.headers.get('content-type')
      if (contentType && contentType.includes('application/json')) {
        return await response.json()
      }
      
      return await response.text()
    } catch (error) {
      clearTimeout(timeoutId)
      
      if (error.name === 'AbortError') {
        throw new Error('请求超时')
      }
      
      // 处理CORS错误,重新抛出错误让上层处理
      if (error.toString().includes('CORS') || error.toString().includes('Failed to fetch')) {
        console.error('CORS错误:', url, error)
        throw new Error('网络请求失败,请检查网络连接')
      }
      
      throw error
    }
  }

  // GET请求
  get(url, params = {}) {
    let fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`
    
    // 统一使用手动构建查询字符串的方式,避免URL对象可能带来的问题
    const queryString = Object.entries(params)
      .filter(([_, value]) => value !== undefined && value !== null)
      .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
      .join('&')
    
    fullUrl = queryString ? `${fullUrl}?${queryString}` : fullUrl

    return this.request(fullUrl, {
      method: 'GET'
    })
  }

  // POST请求
  post(url, data = {}) {
    return this.request(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        ...this.headers
      },
      body: JSON.stringify(data)
    })
  }

  // PUT请求
  put(url, data = {}) {
    return this.request(url, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        ...this.headers
      },
      body: JSON.stringify(data)
    })
  }

  // DELETE请求
  delete(url) {
    return this.request(url, {
      method: 'DELETE'
    })
  }
}

// 创建默认实例
export const apiClient = new ApiClient()

// 导出类以便创建自定义实例
export { ApiClient }