whistle.mock-plugins 1.0.5 → 1.0.6-beta.0

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.
@@ -10,150 +10,207 @@ const INTERFACES_FILE = path.join(DATA_DIR, 'interfaces.json');
10
10
  // 添加功能模块配置文件
11
11
  const FEATURES_FILE = path.join(DATA_DIR, 'features.json');
12
12
 
13
+ // 配置项
14
+ const CONFIG = {
15
+ // 日志级别:0=NONE, 1=ERROR, 2=WARN, 3=INFO, 4=DEBUG
16
+ LOG_LEVEL: process.env.MOCK_PLUGIN_LOG_LEVEL || 3,
17
+ // 缓存有效期(毫秒),默认 60 秒
18
+ CACHE_INTERVAL: process.env.MOCK_PLUGIN_CACHE_INTERVAL || 60000,
19
+ // 是否在控制台输出详细日志
20
+ VERBOSE_CONSOLE: process.env.MOCK_PLUGIN_VERBOSE === 'true'
21
+ };
22
+
23
+ // 缓存控制变量
24
+ let enabledInterfacesCache = null; // 仅缓存启用状态的接口
25
+ let cacheTime = 0;
26
+ let cacheVersion = 1;
27
+
28
+ // 正则表达式缓存
29
+ const regexCache = new Map();
30
+
13
31
  // 记录日志
14
- const log = (message) => {
15
- console.log(`[mock-plugin:rulesServer] ${message}`);
32
+ const log = (message, level = 3) => {
33
+ // 只记录小于等于当前日志级别的日志
34
+ if (level <= CONFIG.LOG_LEVEL) {
35
+ const prefix = level === 1 ? '[ERROR] ' :
36
+ level === 2 ? '[WARN] ' :
37
+ level === 4 ? '[DEBUG] ' : '';
38
+
39
+ console.log(`[mock-plugin:rulesServer] ${prefix}${message}`);
40
+ }
16
41
  };
17
42
 
18
- // 加载功能模块配置
19
- const loadFeatures = () => {
43
+ // 错误日志
44
+ const logError = (message) => log(message, 1);
45
+
46
+ // 警告日志
47
+ const logWarn = (message) => log(message, 2);
48
+
49
+ // 信息日志
50
+ const logInfo = (message) => log(message, 3);
51
+
52
+ // 调试日志
53
+ const logDebug = (message) => log(message, 4);
54
+
55
+ // 获取文件最后修改时间
56
+ const getFileModTime = (filePath) => {
20
57
  try {
21
- if (!fs.existsSync(FEATURES_FILE)) {
22
- log('功能模块配置文件不存在');
23
- return [];
24
- }
25
-
26
- const data = fs.readJsonSync(FEATURES_FILE);
27
-
28
- // 处理不同的数据结构
29
- let features = [];
30
-
31
- if (data && Array.isArray(data)) {
32
- // 如果直接是数组
33
- features = data;
34
- } else if (data && data.features && Array.isArray(data.features)) {
35
- // 如果是 {features: [...]} 格式
36
- features = data.features;
37
- } else {
38
- log('未能识别功能模块数据结构');
39
- return [];
40
- }
41
-
42
- log(`总共发现 ${features.length} 个功能模块配置`);
43
-
44
- // 过滤出启用状态的功能模块
45
- const enabledFeatures = features.filter(feature => feature.active === true);
46
-
47
- log(`找到 ${enabledFeatures.length} 个启用的功能模块`);
48
- return enabledFeatures;
58
+ const stats = fs.statSync(filePath);
59
+ return stats.mtimeMs;
49
60
  } catch (err) {
50
- log(`加载功能模块配置失败: ${err.message}`);
51
- return [];
61
+ return 0;
52
62
  }
53
63
  };
54
64
 
55
- // 加载接口配置
56
- const loadInterfaces = () => {
65
+ // 加载所有启用的接口(整合了功能模块和接口的加载逻辑)
66
+ const loadEnabledInterfaces = () => {
57
67
  try {
58
- if (!fs.existsSync(INTERFACES_FILE)) {
59
- log('接口配置文件不存在');
60
- return [];
61
- }
62
-
63
- const data = fs.readJsonSync(INTERFACES_FILE);
64
- // 为了避免日志过长,只打印前100个字符
65
- const dataPreview = JSON.stringify(data).substring(0, 100) + (JSON.stringify(data).length > 100 ? '...' : '');
66
- log(`读取到接口配置数据: ${dataPreview}`);
68
+ const currentTime = Date.now();
69
+ const interfacesFileModTime = getFileModTime(INTERFACES_FILE);
70
+ const featuresFileModTime = getFileModTime(FEATURES_FILE);
67
71
 
68
- // 处理不同的数据结构
69
- let interfaces = [];
72
+ // 取两个文件的最后修改时间的最大值
73
+ const lastModTime = Math.max(interfacesFileModTime, featuresFileModTime);
70
74
 
71
- if (data && Array.isArray(data)) {
72
- // 如果直接是数组
73
- interfaces = data;
74
- } else if (data && data.interfaces && Array.isArray(data.interfaces)) {
75
- // 如果是 {interfaces: [...]} 格式
76
- interfaces = data.interfaces;
77
- } else {
78
- log('未能识别接口数据结构');
79
- return [];
75
+ // 如果缓存有效且文件未被修改,直接返回缓存
76
+ if (enabledInterfacesCache &&
77
+ currentTime - cacheTime < CONFIG.CACHE_INTERVAL &&
78
+ lastModTime <= cacheTime) {
79
+ logDebug('使用已启用接口的缓存');
80
+ return enabledInterfacesCache;
80
81
  }
81
82
 
82
- log(`总共发现 ${interfaces.length} 个接口配置`);
83
-
84
- // 加载功能模块配置
85
- const enabledFeatures = loadFeatures();
86
- const enabledFeatureIds = enabledFeatures.map(feature => feature.id);
83
+ logDebug('缓存无效或文件已修改,重新加载接口');
87
84
 
88
- // 过滤出启用状态的接口
89
- // 兼容不同字段: active 或 enabled
90
- const enabledInterfaces = interfaces.filter(intf => {
91
- // 首先检查接口本身是否启用
92
- const isInterfaceEnabled = intf.enabled === true || intf.active === true;
93
-
94
- // 如果接口未启用,直接排除
95
- if (!isInterfaceEnabled) {
96
- return false;
85
+ // 1. 读取并解析所有功能模块
86
+ let enabledFeatureIds = [];
87
+ if (fs.existsSync(FEATURES_FILE)) {
88
+ try {
89
+ const featuresData = fs.readJsonSync(FEATURES_FILE);
90
+ let features = [];
91
+
92
+ if (featuresData && Array.isArray(featuresData)) {
93
+ features = featuresData;
94
+ } else if (featuresData && featuresData.features && Array.isArray(featuresData.features)) {
95
+ features = featuresData.features;
96
+ }
97
+
98
+ // 过滤出启用状态的功能ID
99
+ enabledFeatureIds = features
100
+ .filter(feature => feature.active === true)
101
+ .map(feature => feature.id);
102
+
103
+ logDebug(`找到 ${enabledFeatureIds.length} 个启用的功能模块`);
104
+ } catch (err) {
105
+ logError(`读取功能模块失败: ${err.message}`);
97
106
  }
98
-
99
- // 如果接口有关联功能,检查功能是否启用
100
- const featureId = intf.featureId;
101
- if (featureId) {
102
- return enabledFeatureIds.includes(featureId);
107
+ }
108
+
109
+ // 2. 读取并解析所有接口
110
+ let enabledInterfaces = [];
111
+ if (fs.existsSync(INTERFACES_FILE)) {
112
+ try {
113
+ const interfacesData = fs.readJsonSync(INTERFACES_FILE);
114
+ let interfaces = [];
115
+
116
+ if (interfacesData && Array.isArray(interfacesData)) {
117
+ interfaces = interfacesData;
118
+ } else if (interfacesData && interfacesData.interfaces && Array.isArray(interfacesData.interfaces)) {
119
+ interfaces = interfacesData.interfaces;
120
+ }
121
+
122
+ // 过滤出启用状态的接口
123
+ enabledInterfaces = interfaces.filter(intf => {
124
+ // 首先检查接口本身是否启用
125
+ const isInterfaceEnabled = intf.active !== false; // 默认为true
126
+
127
+ if (!isInterfaceEnabled) {
128
+ return false;
129
+ }
130
+
131
+ // 如果接口有关联功能,检查功能是否启用
132
+ if (intf.featureId) {
133
+ return enabledFeatureIds.includes(intf.featureId);
134
+ }
135
+
136
+ // 如果接口没有关联功能,只要接口本身启用就可以
137
+ return true;
138
+ });
139
+
140
+ logInfo(`找到 ${enabledInterfaces.length} 个启用的接口`);
141
+ } catch (err) {
142
+ logError(`读取接口配置失败: ${err.message}`);
103
143
  }
104
-
105
- // 如果接口没有关联功能,只要接口本身启用就可以
106
- return true;
107
- });
144
+ }
145
+
146
+ // 更新缓存
147
+ enabledInterfacesCache = enabledInterfaces;
148
+ cacheTime = currentTime;
108
149
 
109
- log(`找到 ${enabledInterfaces.length} 个启用的接口配置`);
110
150
  return enabledInterfaces;
111
151
  } catch (err) {
112
- log(`加载接口配置失败: ${err.message}`);
113
- return [];
152
+ logError(`加载启用接口失败: ${err.message}`);
153
+ return enabledInterfacesCache || [];
114
154
  }
115
155
  };
116
156
 
117
157
  // 匹配URL是否符合模式
118
158
  const isUrlMatch = (url, pattern, proxyType, fullUrl) => {
119
159
  if (!pattern) return false;
120
-
160
+
121
161
  try {
122
162
  // 针对不同的代理类型采用不同的匹配策略
123
163
 
124
164
  // 对于url_redirect类型,需要完全匹配fullPath才命中
125
165
  if (proxyType === 'url_redirect') {
126
- // 精确匹配完整路径
166
+ // 精确匹配完整路径,这是最快的比较方式
127
167
  return pattern === fullUrl;
128
168
  }
129
169
 
130
170
  // 对于redirect类型,只要url以pattern开头即可命中(前缀匹配)
131
171
  if (proxyType === 'redirect') {
132
- return fullUrl.indexOf(pattern) === 0;
172
+ // 使用字符串原生的startsWith方法更高效
173
+ return fullUrl.startsWith(pattern);
133
174
  }
134
175
 
135
176
  // 以下是默认的匹配逻辑(用于response类型等)
136
177
 
137
- // 精确匹配
178
+ // 精确匹配 - 最高效的匹配方式
138
179
  if (pattern === url) {
139
180
  return true;
140
181
  }
141
182
 
142
183
  // 通配符匹配
143
184
  if (pattern.includes('*')) {
144
- // 需要转换为正则表达式,处理特殊字符
145
- const regexPattern = pattern
146
- .replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // 转义特殊字符
147
- .replace(/\*/g, '.*'); // 将 * 替换为 .*
185
+ // 使用缓存的正则表达式
186
+ const cacheKey = `wildcard:${pattern}`;
187
+ let regex = regexCache.get(cacheKey);
188
+
189
+ if (!regex) {
190
+ // 需要转换为正则表达式,处理特殊字符
191
+ const regexPattern = pattern
192
+ .replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // 转义特殊字符
193
+ .replace(/\*/g, '.*'); // 将 * 替换为 .*
194
+
195
+ regex = new RegExp('^' + regexPattern + '$');
196
+ regexCache.set(cacheKey, regex);
197
+ }
148
198
 
149
- const regex = new RegExp('^' + regexPattern + '$');
150
199
  return regex.test(url);
151
200
  }
152
201
 
153
202
  // 正则表达式匹配
154
- if (pattern.startsWith('/') && pattern.endsWith('/')) {
155
- const regexStr = pattern.slice(1, -1);
156
- const regex = new RegExp(regexStr);
203
+ if (pattern.startsWith('/') && pattern.length > 2 && pattern.endsWith('/')) {
204
+ // 使用缓存的正则表达式
205
+ const cacheKey = `regex:${pattern}`;
206
+ let regex = regexCache.get(cacheKey);
207
+
208
+ if (!regex) {
209
+ const regexStr = pattern.slice(1, -1);
210
+ regex = new RegExp(regexStr);
211
+ regexCache.set(cacheKey, regex);
212
+ }
213
+
157
214
  return regex.test(url);
158
215
  }
159
216
  } catch (e) {
@@ -200,8 +257,9 @@ const generateRandomValue = (pattern) => {
200
257
  }
201
258
 
202
259
  const formatPattern = pattern.substring(1); // 去掉@前缀
203
- // 为每个x生成一个随机字符(字母或数字)
260
+ // 使用单次正则替换代替每个字符的遍历
204
261
  return formatPattern.replace(/x/g, () => {
262
+ // 使用一个固定的字符集,避免生成不兼容的字符
205
263
  const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
206
264
  return chars.charAt(Math.floor(Math.random() * chars.length));
207
265
  });
@@ -209,44 +267,93 @@ const generateRandomValue = (pattern) => {
209
267
 
210
268
  // 构建重定向规则
211
269
  const buildRedirectRule = (fullUrl, targetUrl, customHeaders, pattern, isUrlRedirect = 0) => {
270
+ // 构建目标URL
271
+ let finalTargetUrl;
212
272
  if(!isUrlRedirect){
213
- targetUrl = fullUrl.replace(pattern, targetUrl);
273
+ finalTargetUrl = fullUrl.replace(pattern, targetUrl);
214
274
  }else{
215
- targetUrl = `redirect://${targetUrl}`;
275
+ finalTargetUrl = `redirect://${targetUrl}`;
216
276
  }
217
277
 
218
- let rule = `${fullUrl} ${targetUrl}`;
278
+ // 定义结果数组,避免多次字符串拼接
279
+ const rules = [`${fullUrl} ${finalTargetUrl}`];
219
280
 
220
281
  // 如果有自定义请求头,添加到规则中
221
282
  if (customHeaders && Object.keys(customHeaders).length > 0) {
222
- // 构建headerReplace规则,格式: headerReplace://req.header-name:pattern1=replacement1&pattern2=replacement2
223
- const headerRuleLines = Object.entries(customHeaders).map(([key, value]) => {
283
+ // 为每个请求头创建一个规则行
284
+ Object.entries(customHeaders).forEach(([key, value]) => {
224
285
  // 处理随机值
225
286
  const processedValue = generateRandomValue(value);
226
287
 
227
- return `${fullUrl} headerReplace://req.${key}:/(.*)/=${processedValue}`;
288
+ // 添加到规则数组
289
+ rules.push(`${fullUrl} headerReplace://req.${key}:/(.*)/=${processedValue}`);
228
290
  });
229
-
230
- if (headerRuleLines.length > 0) {
231
- // 将所有规则合并为一个多行规则字符串
232
- rule = `${rule}\n${headerRuleLines.join('\n')}`;
233
- }
234
291
  }
235
292
 
236
- return rule;
293
+ // 最后才将所有规则合并为一个字符串,减少字符串操作
294
+ return rules.join('\n');
295
+ };
296
+
297
+ // 缓存管理接口 - 简化版本
298
+ const cacheManager = {
299
+ // 清除缓存
300
+ clearCache: () => {
301
+ logInfo('主动清除接口缓存');
302
+ enabledInterfacesCache = null;
303
+ cacheTime = 0;
304
+ cacheVersion++;
305
+ },
306
+
307
+ // 获取当前缓存状态
308
+ getStatus: () => {
309
+ return {
310
+ enabled: {
311
+ cached: !!enabledInterfacesCache,
312
+ cacheTime: cacheTime,
313
+ itemCount: enabledInterfacesCache ? enabledInterfacesCache.length : 0
314
+ },
315
+ cacheVersion,
316
+ configInterval: CONFIG.CACHE_INTERVAL
317
+ };
318
+ }
237
319
  };
238
320
 
239
321
  // Whistle 规则服务器实现
240
322
  module.exports = (server, options) => {
323
+ // 优化:缓存常用路径匹配
324
+ const debugConfigPath = '/_debug/config';
325
+ const debugEnabledInterfacesPath = '/_debug/enabled-interfaces';
326
+ const clearCachePath = '/_flush_cache';
327
+
241
328
  server.on('request', (req, res) => {
242
329
  const oReq = req.originalReq;
243
330
 
244
331
  // 提取请求相关信息
245
332
  const fullUrl = oReq.url;
246
- log(`原始URL: ${fullUrl}`);
333
+ logDebug(`原始URL: ${fullUrl}`);
334
+
335
+ // 处理缓存刷新请求
336
+ if (fullUrl.startsWith(clearCachePath)) {
337
+ cacheManager.clearCache();
338
+
339
+ res.writeHead(200, { 'Content-Type': 'application/json' });
340
+ res.end(JSON.stringify({
341
+ success: true,
342
+ message: '缓存已刷新',
343
+ cacheStatus: cacheManager.getStatus()
344
+ }));
345
+ return;
346
+ }
347
+
348
+ // 特殊调试路径,查看缓存状态
349
+ if (fullUrl.endsWith('/_cache_status')) {
350
+ res.writeHead(200, { 'Content-Type': 'application/json' });
351
+ res.end(JSON.stringify(cacheManager.getStatus()));
352
+ return;
353
+ }
247
354
 
248
- // 特殊调试路径,用于查看配置文件内容
249
- if (fullUrl.endsWith('/_debug/config')) {
355
+ // 特殊调试路径,使用更高效的匹配
356
+ if (fullUrl.endsWith(debugConfigPath)) {
250
357
  try {
251
358
  const configs = {
252
359
  interfaces: fs.existsSync(INTERFACES_FILE) ? fs.readJsonSync(INTERFACES_FILE) : null,
@@ -255,23 +362,23 @@ module.exports = (server, options) => {
255
362
  res.end(JSON.stringify(configs, null, 2));
256
363
  return;
257
364
  } catch (err) {
258
- log(`读取配置文件失败: ${err.message}`);
365
+ logError(`读取配置文件失败: ${err.message}`);
259
366
  res.end(`读取配置文件失败: ${err.message}`);
260
367
  return;
261
368
  }
262
369
  }
263
370
 
264
371
  // 调试路径,查看启用的接口
265
- if (fullUrl.endsWith('/_debug/enabled-interfaces')) {
372
+ if (fullUrl.endsWith(debugEnabledInterfacesPath)) {
266
373
  try {
267
- const enabledInterfaces = loadInterfaces();
374
+ const enabledInterfaces = loadEnabledInterfaces();
268
375
  res.end(JSON.stringify({
269
376
  count: enabledInterfaces.length,
270
377
  interfaces: enabledInterfaces
271
378
  }, null, 2));
272
379
  return;
273
380
  } catch (err) {
274
- log(`获取启用接口失败: ${err.message}`);
381
+ logError(`获取启用接口失败: ${err.message}`);
275
382
  res.end(`获取启用接口失败: ${err.message}`);
276
383
  return;
277
384
  }
@@ -282,7 +389,7 @@ module.exports = (server, options) => {
282
389
  try {
283
390
  parsedUrl = url.parse(fullUrl);
284
391
  } catch (err) {
285
- log(`解析URL失败: ${err.message}`);
392
+ logError(`解析URL失败: ${err.message}`);
286
393
  // 解析失败时,不处理请求
287
394
  res.end('');
288
395
  return;
@@ -292,26 +399,25 @@ module.exports = (server, options) => {
292
399
  const method = oReq.method;
293
400
  const path = parsedUrl.pathname || '';
294
401
 
295
- log(`请求方法: ${method}, 请求路径: ${path}`);
402
+ logDebug(`请求方法: ${method}, 请求路径: ${path}`);
296
403
 
297
- // 加载启用的接口配置
298
- const enabledInterfaces = loadInterfaces();
299
- log(`已加载 ${enabledInterfaces.length} 个接口配置`);
404
+ // 加载启用的接口配置(使用缓存优化)
405
+ const enabledInterfaces = loadEnabledInterfaces();
300
406
 
301
407
  if (enabledInterfaces.length === 0) {
302
- log('没有找到启用的接口配置,跳过处理');
408
+ logDebug('没有找到启用的接口配置,跳过处理');
303
409
  res.end('');
304
410
  return;
305
411
  }
306
412
 
307
- // 检查是否有匹配的接口
413
+ // 优化:使用 find 查找匹配的接口,只遍历一次数组
308
414
  const matchedInterface = enabledInterfaces.find(intf => {
309
415
  // 获取URL模式
310
416
  const pattern = intf.urlPattern || '';
311
417
  const proxyType = intf.proxyType || 'response';
312
418
 
313
419
  if (!pattern) {
314
- log(`接口 ${intf.id || intf.name || '未知'} 缺少URL模式`);
420
+ logDebug(`接口 ${intf.id || intf.name || '未知'} 缺少URL模式`);
315
421
  return false;
316
422
  }
317
423
 
@@ -323,7 +429,7 @@ module.exports = (server, options) => {
323
429
  return false;
324
430
  }
325
431
 
326
- log(`URL模式匹配成功: ${fullUrl} 匹配 ${pattern} (${proxyType}模式)`);
432
+ logDebug(`URL模式匹配成功: ${fullUrl} 匹配 ${pattern} (${proxyType}模式)`);
327
433
 
328
434
  // 检查HTTP方法匹配
329
435
  const methodField = intf.httpMethod || intf.method || '';
@@ -334,18 +440,18 @@ module.exports = (server, options) => {
334
440
  methodValue === method;
335
441
 
336
442
  if (!methodMatches) {
337
- log(`方法不匹配: 请求方法 ${method}, 接口方法 ${methodValue}`);
443
+ logDebug(`方法不匹配: 请求方法 ${method}, 接口方法 ${methodValue}`);
338
444
  return false;
339
445
  }
340
446
 
341
- log(`方法匹配成功: ${method} 匹配 ${methodValue || 'ALL'}`);
447
+ logDebug(`方法匹配成功: ${method} 匹配 ${methodValue || 'ALL'}`);
342
448
 
343
449
  return true;
344
450
  });
345
451
 
346
452
  // 如果找到匹配的接口,处理对应的规则
347
453
  if (matchedInterface) {
348
- log(`找到匹配接口 "${matchedInterface.name || matchedInterface.id || '未知'}" 用于 ${method} ${path}`);
454
+ logInfo(`找到匹配接口 "${matchedInterface.name || matchedInterface.id || '未知'}" 用于 ${method} ${path}`);
349
455
 
350
456
  // 处理不同的代理类型
351
457
  const proxyType = matchedInterface.proxyType || 'response';
@@ -353,14 +459,14 @@ module.exports = (server, options) => {
353
459
  // 优先级判断:首先处理 response 类型
354
460
  if (proxyType === 'response') {
355
461
  // 返回指向插件服务的规则
356
- log(`接口 "${matchedInterface.name}" 使用模拟响应模式`);
462
+ logDebug(`接口 "${matchedInterface.name}" 使用模拟响应模式`);
357
463
  res.end(`${fullUrl} mock-plugins://`);
358
464
  return;
359
465
  }
360
466
 
361
467
  // 对于redirect和url_redirect类型,验证规则有效性
362
468
  if ((proxyType === 'redirect' || proxyType === 'url_redirect') && !validateRedirectRule(matchedInterface)) {
363
- log(`接口 "${matchedInterface.name}" 的重定向规则无效,跳过处理`);
469
+ logWarn(`接口 "${matchedInterface.name}" 的重定向规则无效,跳过处理`);
364
470
  res.end('');
365
471
  return;
366
472
  }
@@ -369,18 +475,18 @@ module.exports = (server, options) => {
369
475
  if (proxyType === 'redirect') {
370
476
  const targetUrl = matchedInterface.targetUrl.trim();
371
477
  const pattern = matchedInterface.urlPattern.trim();
372
- log(`接口 "${matchedInterface.name}" 使用重定向模式,模式: ${pattern}, 目标URL: ${targetUrl}`);
478
+ logDebug(`接口 "${matchedInterface.name}" 使用重定向模式,模式: ${pattern}, 目标URL: ${targetUrl}`);
373
479
 
374
480
  // 确保目标URL合法
375
481
  if (!isValidTargetUrl(targetUrl)) {
376
- log(`目标URL不合法: ${targetUrl},跳过处理`);
482
+ logWarn(`目标URL不合法: ${targetUrl},跳过处理`);
377
483
  res.end('');
378
484
  return;
379
485
  }
380
486
 
381
487
  // 执行重定向:将fullUrl中匹配的pattern替换为targetUrl
382
- // 对于redirect类型,我们使用indexOf确认前缀匹配
383
- if (fullUrl.indexOf(pattern) === 0) {
488
+ // 使用startsWith优化匹配效率
489
+ if (fullUrl.startsWith(pattern)) {
384
490
  // 获取自定义请求头
385
491
  const customHeaders = matchedInterface.customHeaders || {};
386
492
 
@@ -389,10 +495,10 @@ module.exports = (server, options) => {
389
495
 
390
496
  // 返回规则
391
497
  res.end(redirectRule);
392
- log(`重定向规则: ${redirectRule}`);
498
+ logDebug(`重定向规则: ${redirectRule}`);
393
499
  return;
394
500
  } else {
395
- log(`URL ${fullUrl} 不以 ${pattern} 开头,跳过处理`);
501
+ logDebug(`URL ${fullUrl} 不以 ${pattern} 开头,跳过处理`);
396
502
  res.end('');
397
503
  return;
398
504
  }
@@ -401,11 +507,11 @@ module.exports = (server, options) => {
401
507
  // 处理 url_redirect 类型:完全匹配 fullPath 并直接返回目标URL
402
508
  if (proxyType === 'url_redirect') {
403
509
  const targetUrl = matchedInterface.targetUrl.trim();
404
- log(`接口 "${matchedInterface.name}" 使用URL重定向模式,目标URL: ${targetUrl}`);
510
+ logDebug(`接口 "${matchedInterface.name}" 使用URL重定向模式,目标URL: ${targetUrl}`);
405
511
 
406
512
  // 确保目标URL合法
407
513
  if (!isValidTargetUrl(targetUrl)) {
408
- log(`目标URL不合法: ${targetUrl},跳过处理`);
514
+ logWarn(`目标URL不合法: ${targetUrl},跳过处理`);
409
515
  res.end('');
410
516
  return;
411
517
  }
@@ -420,21 +526,21 @@ module.exports = (server, options) => {
420
526
 
421
527
  // 返回规则
422
528
  res.end(redirectRule);
423
- log(`URL重定向规则: ${redirectRule}`);
529
+ logDebug(`URL重定向规则: ${redirectRule}`);
424
530
  return;
425
531
  } else {
426
- log(`URL ${fullUrl} 不完全匹配 ${matchedInterface.urlPattern},跳过处理`);
532
+ logDebug(`URL ${fullUrl} 不完全匹配 ${matchedInterface.urlPattern},跳过处理`);
427
533
  res.end('');
428
534
  return;
429
535
  }
430
536
  }
431
537
 
432
538
  // 默认情况下使用mock-plugins处理
433
- log(`接口 "${matchedInterface.name}" 使用默认处理模式`);
539
+ logDebug(`接口 "${matchedInterface.name}" 使用默认处理模式`);
434
540
  res.end(`${fullUrl} mock-plugins://`);
435
541
  } else {
436
542
  // 如果没有匹配的接口,返回空字符串,Whistle 会继续处理下一个规则
437
- log(`未找到匹配接口,跳过处理 ${method} ${path}`);
543
+ logDebug(`未找到匹配接口,跳过处理 ${method} ${path}`);
438
544
  res.end('');
439
545
  }
440
546
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistle.mock-plugins",
3
- "version": "1.0.5",
3
+ "version": "1.0.6-beta.0",
4
4
  "description": "Whistle 插件,用于快速创建 API 模拟数据",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {