Я использую node-http-proxy в качестве промежуточного программного обеспечения.При обработке GET
запросов это нормально.Для POST
запросов с телом JSON я не могу отправить заголовок application/json
и тело в кодировке json.
Это мое промежуточное ПО.Я добавил опцию reastream, как было предложено, но, похоже, это не решает проблему:
//restream parsed body before proxying
proxy.on('proxyReq', function (proxyReq, req, res, options) {
if (!req.body || !Object.keys(req.body).length) {
return;
}
var contentType = proxyReq.getHeader('Content-Type');
var bodyData;
if (contentType === 'application/json') {
bodyData = JSON.stringify(req.body);
}
if (contentType === 'application/x-www-form-urlencoded') {
bodyData = queryString.stringify(req.body);
}
if (bodyData) {
console.log("------", bodyData);
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
}
});
proxy.on('proxyRes', function (proxyRes, req, res) {
modifyResponse(res, proxyRes, function (response) {
if (!MXMUtil.empty(response) && !MXMUtil.empty(response.message) && !MXMUtil.empty(response.message.body)) { // valid response
if (!MXMUtil.empty(response.message.body.error)) { // error response
var message = self.processor.createErrorMessage(500, '', response.message.body.error);
response = message;
} else { // valid response
var message = self.processor.prepareResponse(req_start, response.message.body);
response = message;
}
}
return response; // return value can be a promise
});
});
proxy.on('error', function (error, req, res) {
var errorMessage = self.processor.createErrorMessage(500, '', error.message);
res.send(errorMessage);
res.end();
});
proxy.web(req, res, {
// url string to be parsed with the url module
target: target_uri,
// true/false, Default: false - changes the origin of the host header to the target URL
changeOrigin: true,
// true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request
ignorePath: true
});
На серверной стороне я получаю эту ошибку:
Malformed HTTP message from 172.17.0.1: Malformed HTTP headers: '\r\n0\r\n\r\n'
Также второй запрос будетошибка с Can't set headers after they are sent.
ошибкой на промежуточном программном обеспечении:
[Tue Sep 25 2018 18:16:29 GMT+0200 (CEST)] trapped error code: message:Can't set headers after they are sent. stack:Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ClientRequest.setHeader (_http_outgoing.js:498:3)
at ProxyServer.<anonymous> (/webservice/lib/api.js:616:34)
at ProxyServer.emit (/webservice/node_modules/eventemitter3/index.js:210:27)
at ClientRequest.<anonymous> (/webservice/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js:132:27)
at emitOne (events.js:121:20)
at ClientRequest.emit (events.js:211:7)
at tickOnSocket (_http_client.js:652:7)
at onSocketNT (_http_client.js:668:5)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickDomainCallback (internal/process/next_tick.js:218:9):
Я использую анализатор тела перед инициализацией промежуточного программного обеспечения:
// express app
this.app = express();
// development || production
this.app.set('env', env);
// custom logging: only error codes
this.app.use(morgan('combined', {
skip: function (req, res) {
return res.statusCode < 400;
}
}));
// LP: handle Error: request entity too large
this.app.use(bodyParser.json({limit: '50mb'}));
this.app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
this.app.use(cookieParser());
this.app.use(compression()); // gzip compression
NOTE. Thisпотенциально может быть ошибкой, и я открыл проблему здесь , но должен быть альтернативный подход.Еще одна связанная проблема: здесь .
Запрос, выданный curl -d {...}
в tornado
, будет работать правильно.
[ОБНОВЛЕНИЕ]
Я исправил проблему Can't set headers after they are sent.
, закрывающую запрос после записи данных:
proxyReq.write(bodyData);
proxyReq.end();
Я попытался предложить решение поставить bodyParser
после использования прокси, но не могусделать это в моей архитектуре, это означает, что в моем случае порядок выглядит следующим образом:
httpProxy = require('http-proxy'),
proxy = httpProxy.createProxyServer({});
// custom logging: only error codes
this.app.use(morgan('combined', {
skip: function (req, res) {
return res.statusCode < 400;
}
}));
// LP: handle Error: request entity too large
this.app.use(bodyParser.json({ limit: '50mb' }));
this.app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
this.app.use(cookieParser());
this.app.use(compression()); // gzip compression
//...api routes...
this.app.post(self._options.baseUrl.uri, function (req, res) {
var req_start = new Date().getTime();
var target_uri = self._options.proxy.uri;
//restream parsed body before proxying
proxy.on('proxyReq', function (proxyReq, req, res, options) {
if (!req.body || !Object.keys(req.body).length) {
return;
}
var bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.write(bodyData);
proxyReq.end();
});
proxy.on('proxyRes', function (proxyRes, req, res) {
modifyResponse(res, proxyRes, function (response) {
console.log("--response--->", response)
if (!MXMUtil.empty(response) && !MXMUtil.empty(response.message) && !MXMUtil.empty(response.message.body)) { // valid response
if (!MXMUtil.empty(response.message.body.error)) { // error response
var message = self.processor.createErrorMessage(500, '', response.message.body.error);
response = message;
} else { // valid response
var message = self.processor.prepareResponse(req_start, response.message.body);
response = message;
}
}
return response; // return value can be a promise
});
});
proxy.on('error', function (error, req, res) {
var errorMessage = self.processor.createErrorMessage(500, '', error.message);
res.send(errorMessage);
res.end();
});
proxy.web(req, res, {
// url string to be parsed with the url module
target: target_uri,
// true/false, Default: false - changes the origin of the host header to the target URL
changeOrigin: true,
// true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request
ignorePath: true
});
});
Как отмечено в комментариях ниже, бывает, что прокси-запрос отправляется дважды, как для кода выше, поэтому я могусм дважды войти в события proxyReq
и proxyRes
.