whistle 如何使用脚本处理请求和响应,有哪些高级用法?
答案Whistle 支持通过脚本处理请求和响应,这为开发者提供了极大的灵活性,可以实现复杂的网络请求处理逻辑。脚本处理基础1. 脚本文件格式Whistle 脚本使用 CommonJS 模块格式:module.exports = function(req, res) { // 处理逻辑};2. 脚本类型reqScript:处理请求resScript:处理响应plugin:插件脚本reqScript 使用1. 基本用法创建脚本文件:request-handler.jsmodule.exports = function(req, res) { console.log('Request URL:', req.url); console.log('Request Method:', req.method); console.log('Request Headers:', req.headers);};配置规则:www.example.com reqScript://{request-handler.js}2. 修改请求头module.exports = function(req, res) { // 添加自定义请求头 req.headers['X-Custom-Header'] = 'Custom Value'; // 修改现有请求头 req.headers['User-Agent'] = 'Custom User Agent'; // 删除请求头 delete req.headers['Authorization'];};3. 修改请求体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. 请求重定向module.exports = function(req, res) { // 修改请求 URL if (req.url.includes('/old-path')) { req.url = req.url.replace('/old-path', '/new-path'); }};5. 条件处理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.jsmodule.exports = function(req, res) { console.log('Response Status:', res.statusCode); console.log('Response Headers:', res.headers);};配置规则:www.example.com resScript://{response-handler.js}2. 修改响应头module.exports = function(req, res) { // 添加自定义响应头 res.setHeader('X-Custom-Header', 'Custom Value'); // 修改现有响应头 res.setHeader('Content-Type', 'application/json'); // 删除响应头 res.removeHeader('X-Powered-By');};3. 修改响应体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. 响应数据转换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. 响应缓存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 和 resScriptrequest-handler.js:module.exports = function(req, res) { // 请求开始时间 req.startTime = Date.now(); req.headers['X-Request-Start'] = req.startTime;};response-handler.js: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); } };};配置规则:www.example.com reqScript://{request-handler.js} resScript://{response-handler.js}2. 使用外部模块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. 异步处理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.logmodule.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. 错误处理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. 安全考虑验证输入数据防止注入攻击不暴露敏感信息