Изменить тело запроса как JSON с промежуточным ПО прокси Node.js - PullRequest
0 голосов
/ 25 сентября 2018

Я использую 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.

...