Node.js代理转发带请求体的GET请求时触发socket hang up错误
嗨,我之前也碰到过类似的问题,咱们来拆解一下原因和解决方案~
首先得明确一个关键点:虽然HTTP标准没有完全禁止GET请求携带请求体,但绝大多数服务器(包括Node.js内置的http模块服务端)都会默认拒绝这种请求。当服务器收到带body的GET请求时,它会认为这是一个不符合规范的请求,直接关闭连接,这就是你看到Error: socket hang up的根本原因——你的代理还在等着目标服务器返回响应,结果连接被提前断开了。
结合你的代码来看,你直接把body写入了GET请求里发送给目标服务器,但对方不买账,直接断了连接,触发了代理请求的error事件。
接下来给你两个可行的解决方案,你可以根据业务场景选择:
1. 优先遵循HTTP规范,改用POST请求携带body
这是最稳妥、兼容性最好的方案,毕竟几乎所有服务器都支持POST请求带请求体。你可以在代理逻辑里加个判断:如果当前是GET请求且存在非空的body,就把请求方法改成POST。
修改后的代码示例:
const headers = {...req.headers}; // 避免修改原对象 delete headers['content-length']; const body = '...maybe some content...'; // 判断是否需要切换请求方法 const targetMethod = (req.method === 'GET' && body && body.trim().length > 0) ? 'POST' : req.method; // 如果切换成POST,记得更新Content-Length header(如果需要的话) if (targetMethod === 'POST' && body) { headers['content-length'] = Buffer.byteLength(body).toString(); } const chunks: any[] = []; const request = http .request( url, { method: targetMethod, headers, }, (msg) => { msg.on('data', (data) => chunks.push(data)); }, ) .on('error', (error) => { reject(error); }) .on('close', () => { resolve(Buffer.concat(chunks).toString('utf8')); }); request.write(body, (error) => { if (error) { request.destroy(); // 出错时销毁请求,避免资源泄漏 reject(error); } }); request.end();
当然,这需要目标服务器的对应接口支持POST请求,如果接口只允许GET,那这个方法就不适用了。
2. 若必须使用GET带请求体,确保目标服务器支持处理
如果业务上强制要求用GET带body,那你得先修改目标服务器的代码,让它能正确读取GET请求的body。
针对Node.js原生http服务
默认情况下,Node.js的http服务器不会为GET请求触发data事件,你需要手动监听:
const http = require('http'); http.createServer((req, res) => { let requestBody = ''; // 不管请求方法是什么,都监听data和end事件读取body req.on('data', (chunk) => { requestBody += chunk.toString(); }); req.on('end', () => { console.log(`收到${req.method}请求的body:`, requestBody); res.writeHead(200); res.end('处理完成'); }); }).listen(3000);
针对Express框架
默认的body-parser中间件不会解析GET请求的body,你需要手动配置中间件让它处理所有请求方法:
const express = require('express'); const app = express(); // 用text中间件处理所有请求的body(包括GET) app.use(express.text({ type: '*/*' })); app.get('/your-api', (req, res) => { console.log('GET请求的body:', req.body); res.send('OK'); }); app.listen(3000);
只有当目标服务器能正确接收并处理GET请求的body后,你的代理转发才不会出现socket hang up的问题。
最后提个小细节:你的代码里在request.write的回调中如果出错,最好调用request.destroy()销毁请求,避免不必要的资源占用,我已经在上面的示例代码里加上了这个处理。
备注:内容来源于stack exchange,提问作者donnikitos

