-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathapiClient.js
More file actions
138 lines (123 loc) · 5.26 KB
/
Copy pathapiClient.js
File metadata and controls
138 lines (123 loc) · 5.26 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
(() => {
/**
* 灾害预警插件的通用网络请求工具。
*
* 核心设计要点:
* 1. 响应结构自动解包 (unwrapApiResponse):为了简化前台业务组件对数据字典的读取,
* 当检测到后端返回了标准的包裹式结构 { success: true, data: ... } 时,
* 自动剥离外层的 success 状态并将核心的 data 部分解包返回;对于非标准数据则原样透传。
* 2. 查询参数处理 (buildUrl):支持解析数组形式的参数。例如多数据源多选 [A, B] 会被自动合并为 A,B 的字符串格式拼接,
* 同时自动过滤空字符串、null 及 undefined 异常属性,保证请求 URL 地址整洁。
* 3. 错误统一拦截:当网络响应状态码非 2xx 时,自动提取 JSON 回包中的具体错误文案并打包抛出,
* 方便下级捕获后在界面渲染报错占位卡片。
*/
const API_BASE = '/api';
/**
* 判定目标对象是否为普通对象类型
*/
function isPlainObject(value) {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
/**
* 对网络返回数据进行提取和解包
*/
function unwrapApiResponse(payload) {
if (isPlainObject(payload) && payload.success === true && payload.data !== undefined) {
return payload.data;
}
return payload;
}
/**
* 构建带有查询参数的完整 API 请求路径
*/
function buildUrl(endpoint, query = null, baseUrl = '') {
const normalizedEndpoint = String(endpoint || '').startsWith('/')
? String(endpoint || '')
: `/${String(endpoint || '')}`;
const cleanBaseUrl = String(baseUrl || '').replace(/\/$/, '');
const url = `${cleanBaseUrl}${API_BASE}${normalizedEndpoint}`;
if (!query || typeof query !== 'object') {
return url;
}
const params = new URLSearchParams();
Object.entries(query).forEach(([key, value]) => {
if (value === undefined || value === null || value === '') return;
if (Array.isArray(value)) {
if (value.length === 0) return;
// 数据源等多选参数,转为逗号相接的序列
params.set(key, value.join(','));
return;
}
params.set(key, String(value));
});
const queryString = params.toString();
return queryString ? `${url}?${queryString}` : url;
}
/**
* 通用 Fetch 网络请求底层封装
*/
async function request(endpoint, options = {}) {
const {
query, // 查询参数字典
baseUrl, // 针对重定向等特需的接口前缀
unwrap = true, // 是否自动执行响应解包
headers,
body,
responseType = 'json', // 新增:指出响应体的数据解析格式,例如 'blob'、'json' 或 'text'
...fetchOptions
} = options || {};
const requestHeaders = { ...(headers || {}) };
let requestBody = body;
// 如果 body 为非表单数据的常规 JavaScript 对象,自动格式化并补全 Content-Type
if (body !== undefined && body !== null && typeof body === 'object' && !(body instanceof FormData)) {
requestHeaders['Content-Type'] = requestHeaders['Content-Type'] || 'application/json';
requestBody = JSON.stringify(body);
}
const response = await fetch(buildUrl(endpoint, query, baseUrl), {
...fetchOptions,
headers: requestHeaders,
body: requestBody,
});
// 统一异常处理前,先拉取其状态码
if (!response.ok) {
let payload = null;
try {
const contentType = response.headers && response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
payload = await response.json();
} else {
const text = await response.text();
payload = text ? { message: text } : null;
}
} catch (e) {
// 忽略解析错误
}
const error = new Error(payload?.error || payload?.message || `API Error: ${response.status} ${response.statusText}`);
error.status = response.status;
error.payload = payload;
throw error;
}
// 解析成功响应内容
if (responseType === 'blob') {
return await response.blob();
} else if (responseType === 'text') {
return await response.text();
}
let payload = null;
const contentType = response.headers && response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
payload = await response.json();
} else {
const text = await response.text();
payload = text ? { message: text } : null;
}
return unwrap ? unwrapApiResponse(payload) : payload;
}
// 绑定至全局
window.DisasterApiClient = {
API_BASE,
request,
buildUrl,
unwrapApiResponse,
};
})();