Express代理服务器下启用请求体解析后无响应的问题排查及解决
代理服务器转发请求后业务服务器启用解析中间件无响应的问题解决
问题场景
部署了2台业务服务器和1台基于Express的代理服务器:
- 当业务服务器(Server 1)启用
express.json()和express.urlencoded()请求体解析中间件时,通过代理转发的请求无法获取响应 - 移除这两个中间件后请求能正常响应,但业务需要保留这些中间件
相关代码
代理服务器 index.js
const express = require('express'); const dotenv = require('dotenv'); const cors = require('cors'); const { router } = require('./routes'); const app = express(); dotenv.config(); const port = process.env.PORT || '3099'; app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(router); app.listen(3099, () => { console.log('Server listnening on port 3099'); });
代理服务器 routes.js
const express = require('express'); const axios = require('axios'); const registry = require('./registry.json'); const router = express.Router(); router.all('/:apiName/:path?', async (req, res) => { try { const method = req.method; const body = req.body; const headers = req.headers; const apiName = req.params.apiName; const path = req.params.path || ''; if (registry.services[apiName]) { const response = await axios({ method, url: registry.services[apiName].url + path, headers, data: body, }); res.json(response.data); } else { res.status(404).json({ error: 'Api is not registered in the registry !', }); } } catch (err) { console.log(err); res.status(500).json({ error: `Proxy server error : ${err}` }); } }); module.exports = { router };
Server 1 index.js
const express = require('express'); const { router } = require('./router'); const cors = require('cors'); const app = express(); app.use(cors()); // 启用这两个中间件后请求无响应 app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.post('/', (req, res) => { const data = req.body; res.status(200).json(data); }); app.listen(3000, () => { console.log('Server 1 listnening on port 3000'); });
问题根源
代理服务器先通过express.json()/express.urlencoded()解析了请求体,此时原请求头中的Content-Length对应原始请求体的大小。但转发时直接将所有原请求头传递给业务服务器,而axios会重新序列化body生成新的请求体,新旧请求体大小不一致,导致业务服务器的解析中间件因Content-Length不匹配,一直等待完整请求体,最终超时无响应。
解决方法
在代理转发请求时,移除原请求头中由axios自动处理的content-length和transfer-encoding字段,避免冲突:
修改代理服务器routes.js中的请求头处理部分:
router.all('/:apiName/:path?', async (req, res) => { try { const method = req.method; const body = req.body; // 复制原请求头并移除自动处理的字段 const headers = { ...req.headers }; delete headers['content-length']; delete headers['transfer-encoding']; const apiName = req.params.apiName; const path = req.params.path || ''; if (registry.services[apiName]) { const response = await axios({ method, url: registry.services[apiName].url + path, headers, data: body, }); res.json(response.data); } else { res.status(404).json({ error: 'Api is not registered in the registry !', }); } } catch (err) { console.log(err); res.status(500).json({ error: `Proxy server error : ${err}` }); } });
原理:axios在发送请求时,会根据传入的data自动计算并设置正确的Content-Length头,同时处理Transfer-Encoding相关逻辑,因此不需要传递原请求的这些字段,避免与自动生成的头冲突。
内容的提问来源于stack exchange,提问作者Mopcho
相关产品推荐
相关产品推荐

