乐闻世界logo
搜索文章和话题

whistle 如何使用脚本处理请求和响应,有哪些高级用法?

2月21日 16:25

答案

Whistle 支持通过脚本处理请求和响应,这为开发者提供了极大的灵活性,可以实现复杂的网络请求处理逻辑。

脚本处理基础

1. 脚本文件格式

Whistle 脚本使用 CommonJS 模块格式:

javascript
module.exports = function(req, res) { // 处理逻辑 };

2. 脚本类型

  • reqScript:处理请求
  • resScript:处理响应
  • plugin:插件脚本

reqScript 使用

1. 基本用法

创建脚本文件:request-handler.js

javascript
module.exports = function(req, res) { console.log('Request URL:', req.url); console.log('Request Method:', req.method); console.log('Request Headers:', req.headers); };

配置规则:

shell
www.example.com reqScript://{request-handler.js}

2. 修改请求头

javascript
module.exports = function(req, res) { // 添加自定义请求头 req.headers['X-Custom-Header'] = 'Custom Value'; // 修改现有请求头 req.headers['User-Agent'] = 'Custom User Agent'; // 删除请求头 delete req.headers['Authorization']; };

3. 修改请求体

javascript
module.exports = function(req, res) { let body = ''; req.on('data', function(chunk) { body += chunk.toString(); }); req.on('end', function() { // 修改请求体 const modifiedBody = body.replace(/old/g, 'new'); // 重新发送修改后的请求 // 注意:这需要特殊处理 }); };

4. 请求重定向

javascript
module.exports = function(req, res) { // 修改请求 URL if (req.url.includes('/old-path')) { req.url = req.url.replace('/old-path', '/new-path'); } };

5. 条件处理

javascript
module.exports = function(req, res) { // 根据请求方法处理 if (req.method === 'POST') { req.headers['X-Request-Type'] = 'POST'; } // 根据请求路径处理 if (req.url.includes('/api/')) { req.headers['X-API-Request'] = 'true'; } // 根据请求头处理 if (req.headers['authorization']) { req.headers['X-Authenticated'] = 'true'; } };

resScript 使用

1. 基本用法

创建脚本文件:response-handler.js

javascript
module.exports = function(req, res) { console.log('Response Status:', res.statusCode); console.log('Response Headers:', res.headers); };

配置规则:

shell
www.example.com resScript://{response-handler.js}

2. 修改响应头

javascript
module.exports = function(req, res) { // 添加自定义响应头 res.setHeader('X-Custom-Header', 'Custom Value'); // 修改现有响应头 res.setHeader('Content-Type', 'application/json'); // 删除响应头 res.removeHeader('X-Powered-By'); };

3. 修改响应体

javascript
module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { let body = chunk.toString(); // 修改响应内容 body = body.replace(/old/g, 'new'); body = body.replace(/error/g, 'success'); // 重新结束响应 originalEnd.call(res, body, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };

4. 响应数据转换

javascript
module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { const body = chunk.toString(); // JSON 数据转换 if (res.headers['content-type'] && res.headers['content-type'].includes('application/json')) { const jsonData = JSON.parse(body); jsonData.timestamp = Date.now(); jsonData.modified = true; originalEnd.call(res, JSON.stringify(jsonData), encoding); } else { originalEnd.call(res, chunk, encoding); } } else { originalEnd.call(res, chunk, encoding); } }; };

5. 响应缓存

javascript
const cache = {}; module.exports = function(req, res) { const cacheKey = req.url; // 检查缓存 if (cache[cacheKey]) { console.log('Using cached response for:', cacheKey); res.end(cache[cacheKey]); return; } // 缓存响应 const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { cache[cacheKey] = chunk.toString(); originalEnd.call(res, chunk, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };

高级脚本处理

1. 组合使用 reqScript 和 resScript

request-handler.js:

javascript
module.exports = function(req, res) { // 请求开始时间 req.startTime = Date.now(); req.headers['X-Request-Start'] = req.startTime; };

response-handler.js:

javascript
module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { // 计算请求耗时 const duration = Date.now() - (req.startTime || 0); res.setHeader('X-Request-Duration', duration); originalEnd.call(res, chunk, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };

配置规则:

shell
www.example.com reqScript://{request-handler.js} resScript://{response-handler.js}

2. 使用外部模块

javascript
const fs = require('fs'); const path = require('path'); module.exports = function(req, res) { // 读取外部文件 const filePath = path.join(__dirname, 'data.json'); const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); // 使用数据修改响应 const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { const body = chunk.toString(); const modifiedBody = body.replace('{{data}}', JSON.stringify(data)); originalEnd.call(res, modifiedBody, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };

3. 异步处理

javascript
const https = require('https'); module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { // 异步处理 https.get('https://api.example.com/data', (response) => { let data = ''; response.on('data', (chunk) => { data += chunk; }); response.on('end', () => { // 使用外部数据修改响应 const body = chunk.toString(); const modifiedBody = body.replace('{{external-data}}', data); originalEnd.call(res, modifiedBody, encoding); }); }).on('error', (err) => { console.error('Error fetching external data:', err); originalEnd.call(res, chunk, encoding); }); } else { originalEnd.call(res, chunk, encoding); } }; };

脚本调试

1. 使用 console.log

javascript
module.exports = function(req, res) { console.log('Request URL:', req.url); console.log('Request Method:', req.method); console.log('Request Headers:', JSON.stringify(req.headers, null, 2)); };

2. 错误处理

javascript
module.exports = function(req, res) { try { // 处理逻辑 const result = processData(req); res.end(JSON.stringify(result)); } catch (error) { console.error('Script error:', error); res.statusCode = 500; res.end(JSON.stringify({ error: error.message })); } };

最佳实践

1. 脚本组织

  • 按功能模块组织脚本
  • 使用有意义的文件名
  • 添加注释说明脚本用途

2. 性能优化

  • 避免在脚本中进行复杂计算
  • 使用缓存减少重复操作
  • 合理使用异步处理

3. 错误处理

  • 添加适当的错误处理
  • 记录错误日志
  • 提供友好的错误信息

4. 安全考虑

  • 验证输入数据
  • 防止注入攻击
  • 不暴露敏感信息
标签:Whistle