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

How does whistle use scripts to process requests and responses, and what are the advanced usages?

2月21日 16:25

Answer

Whistle supports processing requests and responses through scripts, which provides developers with great flexibility to implement complex network request processing logic.

Script Processing Basics

1. Script File Format

Whistle scripts use CommonJS module format:

javascript
module.exports = function(req, res) { // Processing logic };

2. Script Types

  • reqScript: Process requests
  • resScript: Process responses
  • plugin: Plugin scripts

reqScript Usage

1. Basic Usage

Create script file: 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); };

Configure rule:

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

2. Modify Request Headers

javascript
module.exports = function(req, res) { // Add custom request header req.headers['X-Custom-Header'] = 'Custom Value'; // Modify existing request header req.headers['User-Agent'] = 'Custom User Agent'; // Delete request header delete req.headers['Authorization']; };

3. Modify Request Body

javascript
module.exports = function(req, res) { let body = ''; req.on('data', function(chunk) { body += chunk.toString(); }); req.on('end', function() { // Modify request body const modifiedBody = body.replace(/old/g, 'new'); // Resend modified request // Note: This requires special handling }); };

4. Request Redirection

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

5. Conditional Processing

javascript
module.exports = function(req, res) { // Process based on request method if (req.method === 'POST') { req.headers['X-Request-Type'] = 'POST'; } // Process based on request path if (req.url.includes('/api/')) { req.headers['X-API-Request'] = 'true'; } // Process based on request headers if (req.headers['authorization']) { req.headers['X-Authenticated'] = 'true'; } };

resScript Usage

1. Basic Usage

Create script file: response-handler.js

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

Configure rule:

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

2. Modify Response Headers

javascript
module.exports = function(req, res) { // Add custom response header res.setHeader('X-Custom-Header', 'Custom Value'); // Modify existing response header res.setHeader('Content-Type', 'application/json'); // Delete response header res.removeHeader('X-Powered-By'); };

3. Modify Response Body

javascript
module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { let body = chunk.toString(); // Modify response content body = body.replace(/old/g, 'new'); body = body.replace(/error/g, 'success'); // End response with modified content originalEnd.call(res, body, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };

4. Response Data Transformation

javascript
module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { const body = chunk.toString(); // JSON data transformation 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. Response Caching

javascript
const cache = {}; module.exports = function(req, res) { const cacheKey = req.url; // Check cache if (cache[cacheKey]) { console.log('Using cached response for:', cacheKey); res.end(cache[cacheKey]); return; } // Cache response 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); } }; };

Advanced Script Processing

1. Combine reqScript and resScript

request-handler.js:

javascript
module.exports = function(req, res) { // Request start time 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) { // Calculate request duration const duration = Date.now() - (req.startTime || 0); res.setHeader('X-Request-Duration', duration); originalEnd.call(res, chunk, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };

Configure rule:

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

2. Using External Modules

javascript
const fs = require('fs'); const path = require('path'); module.exports = function(req, res) { // Read external file const filePath = path.join(__dirname, 'data.json'); const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); // Use data to modify response 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. Async Processing

javascript
const https = require('https'); module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { // Async processing https.get('https://api.example.com/data', (response) => { let data = ''; response.on('data', (chunk) => { data += chunk; }); response.on('end', () => { // Use external data to modify response 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); } }; };

Script Debugging

1. Using 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. Error Handling

javascript
module.exports = function(req, res) { try { // Processing logic 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 })); } };

Best Practices

1. Script Organization

  • Organize scripts by functional modules
  • Use meaningful filenames
  • Add comments to explain script purpose

2. Performance Optimization

  • Avoid complex calculations in scripts
  • Use caching to reduce repeated operations
  • Use async processing appropriately

3. Error Handling

  • Add appropriate error handling
  • Log errors
  • Provide friendly error messages

4. Security Considerations

  • Validate input data
  • Prevent injection attacks
  • Don't expose sensitive information
标签:Whistle