chanjs 2.0.12 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/helper/request.js +150 -0
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用 fetch 请求工具
|
|
3
|
+
* @param {string} url 请求地址
|
|
4
|
+
* @param {Object} options 请求配置
|
|
5
|
+
* @param {string} options.method 请求方法,默认 'GET'
|
|
6
|
+
* @param {Object} options.params URL查询参数
|
|
7
|
+
* @param {Object} options.data 请求体数据
|
|
8
|
+
* @param {Object} options.headers 请求头
|
|
9
|
+
* @param {number} options.timeout 超时时间,默认10000ms
|
|
10
|
+
* @param {boolean} options.parseJson 是否自动解析JSON响应,默认true
|
|
11
|
+
* @returns {Promise<any>} 请求结果
|
|
12
|
+
*/
|
|
13
|
+
export const request = async (url, options = {}) => {
|
|
14
|
+
const defaults = {
|
|
15
|
+
method: 'GET',
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
},
|
|
19
|
+
timeout: 10000, // 毫秒
|
|
20
|
+
parseJson: true,
|
|
21
|
+
params: null,
|
|
22
|
+
data: null,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const config = { ...defaults, ...options };
|
|
26
|
+
const { method, headers, timeout, parseJson, params, data } = config;
|
|
27
|
+
|
|
28
|
+
const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
29
|
+
const upperMethod = method.toUpperCase();
|
|
30
|
+
if (!validMethods.includes(upperMethod)) {
|
|
31
|
+
throw new Error(`不支持的请求方法: ${method}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const requestUrl = new URL(url);
|
|
35
|
+
if (params && typeof params === 'object') {
|
|
36
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
37
|
+
if (value !== undefined && value !== null) {
|
|
38
|
+
requestUrl.searchParams.append(key, value);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ✅ 修复:合并 headers
|
|
44
|
+
const finalHeaders = { ...defaults.headers, ...headers };
|
|
45
|
+
|
|
46
|
+
const fetchOptions = {
|
|
47
|
+
method: upperMethod,
|
|
48
|
+
headers: finalHeaders,
|
|
49
|
+
signal: AbortSignal.timeout(timeout),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (!['GET', 'HEAD', 'OPTIONS'].includes(upperMethod) && data) {
|
|
53
|
+
const contentType = finalHeaders['Content-Type'];
|
|
54
|
+
|
|
55
|
+
if (contentType === 'application/json') {
|
|
56
|
+
fetchOptions.body = JSON.stringify(data);
|
|
57
|
+
} else if (contentType === 'application/x-www-form-urlencoded') {
|
|
58
|
+
const formData = new URLSearchParams();
|
|
59
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
60
|
+
formData.append(key, value);
|
|
61
|
+
});
|
|
62
|
+
fetchOptions.body = formData;
|
|
63
|
+
} else if (contentType === 'multipart/form-data') {
|
|
64
|
+
fetchOptions.body = data;
|
|
65
|
+
delete fetchOptions.headers['Content-Type']; // 让浏览器自动设置 boundary
|
|
66
|
+
} else {
|
|
67
|
+
fetchOptions.body = data;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch(requestUrl.toString(), fetchOptions);
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const error = new Error(`请求失败: ${response.status} ${response.statusText}`);
|
|
76
|
+
error.status = response.status;
|
|
77
|
+
error.statusText = response.statusText;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
error.response = await response.json();
|
|
81
|
+
} catch (e) {
|
|
82
|
+
error.response = await response.text();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return parseJson ? await response.json() : await response.text();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error.name === 'AbortError') {
|
|
91
|
+
const timeoutError = new Error(`请求超时(${timeout}ms)`);
|
|
92
|
+
timeoutError.type = 'TIMEOUT';
|
|
93
|
+
throw timeoutError;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ✅ 不覆盖原 error,只添加分类
|
|
97
|
+
if (!error.type) {
|
|
98
|
+
error.type = 'REQUEST_ERROR';
|
|
99
|
+
}
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// import request from './utils/fetchUtil.js';
|
|
105
|
+
|
|
106
|
+
// // GET请求
|
|
107
|
+
// const getToken = async () => {
|
|
108
|
+
// try {
|
|
109
|
+
// const result = await request('https://api.weixin.qq.com/sns/oauth2/access_token', {
|
|
110
|
+
// method: 'GET',
|
|
111
|
+
// params: {
|
|
112
|
+
// appid: APPID,
|
|
113
|
+
// secret: APPSECRET,
|
|
114
|
+
// code: 'xxx',
|
|
115
|
+
// grant_type: 'authorization_code'
|
|
116
|
+
// }
|
|
117
|
+
// });
|
|
118
|
+
// return result;
|
|
119
|
+
// } catch (error) {
|
|
120
|
+
// console.error('获取token失败:', error);
|
|
121
|
+
// }
|
|
122
|
+
// };
|
|
123
|
+
|
|
124
|
+
// // POST请求
|
|
125
|
+
// const submitData = async (formData) => {
|
|
126
|
+
// try {
|
|
127
|
+
// const result = await request('/api/submit', {
|
|
128
|
+
// method: 'POST',
|
|
129
|
+
// data: formData,
|
|
130
|
+
// headers: {
|
|
131
|
+
// 'Content-Type': 'application/x-www-form-urlencoded'
|
|
132
|
+
// }
|
|
133
|
+
// });
|
|
134
|
+
// return result;
|
|
135
|
+
// } catch (error) {
|
|
136
|
+
// console.error('提交数据失败:', error);
|
|
137
|
+
// }
|
|
138
|
+
// };
|
|
139
|
+
|
|
140
|
+
// // PUT请求
|
|
141
|
+
// const updateUser = async (userId, data) => {
|
|
142
|
+
// try {
|
|
143
|
+
// return await request(`/api/users/${userId}`, {
|
|
144
|
+
// method: 'PUT',
|
|
145
|
+
// data: { name: '新名称' }
|
|
146
|
+
// });
|
|
147
|
+
// } catch (error) {
|
|
148
|
+
// console.error('更新用户失败:', error);
|
|
149
|
+
// }
|
|
150
|
+
// };
|