Request: Não é possível canalizar a partir de um pedido que tenha um corpo POST

Criado em 30 jun. 2015  ·  24Comentários  ·  Fonte: request/request

Estou tentando criar um novo objeto de solicitação canalizando a partir de uma solicitação Express. Isso funciona bem para solicitações GET, mas para solicitações POST que têm um corpo, o corpo não parece ter sido copiado para a nova solicitação. Tentei copiar manualmente sobre o corpo assim:

let pipedReq = req.pipe(request({ url: 'http://www.example.com', form: req.body }));

Isso copia sobre o corpo, mas recebo um erro "escrever após o fim" quando pipedReq é limpo. Pode estar relacionado a # 1659.

É fácil reproduzir o problema com o seguinte aplicativo Express simples:

'use strict';

let express = require('express');
let request = require('request');

let app = express();

app.use('/', (req, res) => {
  req.pipe(request.post({ url: 'http://www.example.com', form: { foo: 'bar' }}));
});

app.listen(process.env.PORT || 3000);

Backtrace:

Error: write after end
    at ClientRequest.OutgoingMessage.write (_http_outgoing.js:413:15)
    at Request.write (/.../node_modules/request/request.js:1354:25)
    at end (/.../node_modules/request/request.js:548:16)
    at Immediate._onImmediate (/.../node_modules/request/request.js:576:7)
    at processImmediate [as _immediateCallback] (timers.js:358:17)

Comentários muito úteis

Eu sei que isso é antigo, mas tive dificuldade em encontrar uma solução adequada para isso e pensei que poderia ser útil para outras pessoas.

'use strict';

let express = require('express');
let request = require('request');

let app = express();

app.use('/', (req, res) => {
  req.pipe(request.post({ url: 'http://www.example.com', form: { foo: 'bar' }}), {end: false}).pipe(res);
});

app.listen(process.env.PORT || 3000);

Conforme mostrado em https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options , adicionar isso faz com que o novo fluxo não seja encerrado automaticamente quando req é encerrado.

Embora isso tenha feito este exemplo funcionar, notei alguns problemas ao criar um proxy para outros métodos usando isso. O corpo de alguma forma faz com que tudo feche adequadamente, mas sem um corpo ele ainda trava. Se estiver usando um analisador de corpo json, isso não deve causar problemas, pois faz com que o corpo seja lido como {} mesmo quando não há corpo, mas sem um ainda pode haver problemas. Minha solução atual é verificar se há um cabeçalho de comprimento de conteúdo na solicitação e usar o objeto de opções, se houver.

Todos 24 comentários

Experimente com este:

var express = require('express')
var request = require('request')

var app = express()

// put it before any other middleware
app.use(function(req, res) {
  req.pipe(
    request[req.method.toLowerCase()]('http://example.com'))
  .pipe(res)
})

app.listen(3000, function () {
  console.log('Express server listening on port 3000')
})

Caso queira modificar o corpo, basta construir uma nova solicitação e canalizar para a resposta.

@simov Não quero modificar o corpo, apenas a URL. Portanto, quero manter os cabeçalhos e outras propriedades da solicitação original, mas canalizar para uma nova solicitação com um novo URL. O problema é que o corpo não é copiado quando a solicitação original é canalizada para a nova (não tenho certeza se isso é um bug) e se eu tentar copiar o corpo sobre mim especificando a propriedade form no construtor request , recebo o erro mencionado acima.

Quando você diz "construir uma nova solicitação", está sugerindo que eu simplesmente crie uma nova solicitação do zero com o corpo que desejo e copie os cabeçalhos da solicitação original, em vez de enviá-los? Posso fazer isso, mas seria menos repetitivo e frágil se pudesse usar o suporte de tubo integrado para copiar a solicitação original no novo.

Ainda acho que um ou ambos os problemas são provavelmente um bug (ou seja, 1) o corpo da solicitação não é copiado durante o encanamento e 2) a exceção mencionada em minha postagem original).

Não sei muito sobre expresso ou solicitação (apenas usando-os para um proxy de aplicativo de página única sem realmente entender como funcionam), mas isso corrigiu o erro write after end para mim:

request.post({ url: 'http://www.example.com', form: { foo: 'bar' }}).pipe(res);

Ao invés de:

req.pipe(request.post({ url: 'http://www.example.com', form: { foo: 'bar' }})).pipe(res);

@matthewgertner experimente meu exemplo de código, ele funciona.

@simov Definitivamente funciona, mas seu exemplo não cobre o caso em que req tem um corpo que desejo copiar. Na verdade, eu estava usando um código quase idêntico ao seu exemplo, mas não consegui lidar com as solicitações POST, pois o corpo não é copiado para o novo objeto de solicitação.

@matthewgertner você pode me dar seu exemplo de código _exact_ que não funciona?

@simov É o exemplo da minha postagem original. Se eu usar este, o corpo não será enviado:

'use strict';

let express = require('express');
let request = require('request');

let app = express();

app.use('/', (req, res) => {
  req.pipe(request.post('http://www.example.com'));
});

app.listen(process.env.PORT || 3000);

Se eu usar isso, recebo a exceção:

'use strict';

let express = require('express');
let request = require('request');

let app = express();

app.use('/', (req, res) => {
  req.pipe(request.post({ url: 'http://www.example.com', form: { foo: 'bar' }}));
});

app.listen(process.env.PORT || 3000);

Você pode substituir http://www.example.com por algum URL gerado em http://requestb.in/ e colar seu link aqui? Deve ser semelhante a http://requestb.in/1mv5l8h1?inspect

Ou você pode usar meu código de exemplo acima também, porque é isso que estou usando para servidor. Então estou executando este script:

var request = require('request')
request.post('http://localhost:3000', {form:{some:'data'}})

Como você pode ver no meu link, enviei form:{some:'data'} .

Deixe-me explicar meu caso de uso. Implementei um proxy de API da web simples (consulte https://github.com/salsita/web-api-proxy). Tudo o que ele faz é receber solicitações HTTP e redirecioná-los para outro host, substituindo os parâmetros de URL, se necessário, por variáveis ​​de ambiente no servidor proxy. O objetivo é evitar a incorporação de chaves secretas no código do cliente.

Ele funciona muito bem para solicitações GET, mas recentemente tive a necessidade de usá-lo para POST também. Portanto, não estou inicializando o corpo da minha solicitação como em seu comentário anterior, pois o corpo vem da solicitação expressa. Seu exemplo mais recente definitivamente funciona, mas isso realmente não me ajuda. O que eu preciso fazer é:

req.pipe(request.post('http://some/new/url'));

Parece-me que se req tiver um corpo, ele não será copiado neste caso. E, de qualquer forma, a verdade é que _não_ preciso modificar o corpo (uma vez que pode haver marcadores que preciso substituir usando variáveis ​​de ambiente), então terei que fazer:

req.pipe(request.post({ url: 'http://some/new/url', form: { some: 'form', data: 'here' }));

Isso faz com que uma exceção seja lançada devido ao que eu suspeito ser um bug em request .

De qualquer forma, posso construir um objeto request completamente novo sozinho, mas queria ter certeza de que não estava faltando nada e também relatar esse problema caso seja um bug.

Não funciona porque você está usando uma solicitação GET sempre. Dê uma olhada no meu exemplo de código novamente.

request[req.method.toLowerCase()]('http://example.com')) define o método de solicitação com base na solicitação de entrada.

Além disso, se você precisar modificar o corpo, novamente desde o meu primeiro comentário:

Caso queira modificar o corpo, basta construir uma nova solicitação e canalizar para a resposta.

Não funciona porque você está sempre usando uma solicitação GET. Dê uma olhada no meu exemplo de código novamente.

Mas estou usando request.post . Isso é a mesma coisa que request['post'] .

Caso queira modificar o corpo, basta construir uma nova solicitação e canalizar para a resposta.

É uma chatice que eu basicamente preciso reescrever o código que já está em request para canalizar solicitações para novas solicitações (copiando cabeçalhos, etc.). Pode haver um monte de casos extremos com os quais eu preciso lidar. Mas sim, acho que é isso que vou acabar fazendo.

OMI deve ser possível canalizar uma solicitação para outra que tenha um corpo de formulário sem que uma exceção seja lançada, ou você acha que há algum motivo para esse comportamento?

Isso é fazer GET solicitação sempre porque você não está especificando nada explicitamente.

A construção de um novo objeto se aplica quando você deseja modificar o corpo.

Claro, a base de código atual não suporta POST. Estou tentando consertar isso, e foi quando me deparei com os problemas descritos aqui. Eu apenas referenciei a base de código existente para explicar melhor o caso de uso.

Tenho quase certeza de que preciso criar um novo objeto sempre, mesmo que não queira modificar o corpo, já que o corpo não é copiado pela operação de tubo. Você tentou o exemplo que forneci? Você precisa de uma solicitação Express com um corpo que canalize para um objeto request . Você verá que o corpo é undefined no novo objeto.

GET pedidos não têm um corpo, isso é por design.

Este funciona (seu exemplo):

'use strict';

let express = require('express');
let request = require('request');

let app = express();

app.use('/', (req, res) => {
  req.pipe(request.post('http://www.example.com'));
});

app.listen(process.env.PORT || 3000);

Usando isso para fazer a solicitação:

var request = require('request')
request.post('http://localhost:3000', {form:{some:'data'}})

some=data foi enviado.

Você verá que o corpo está indefinido no novo objeto.

Onde está isso, eu não estou te seguindo.

Hmmm, ok. Quando eu uso requestb.in (boa dica!), Posso ver que o corpo realmente _é_ enviado. Na verdade, tive (e ainda tenho) dois problemas que me levaram a acreditar que não estava sendo enviado:

1) Quando eu uso cURL para enviar uma solicitação ao meu servidor (usando o código em seu comentário anterior), ele trava após enviar a solicitação (ou seja, cURL não termina).
2) Quando vejo request.body , é undefined passo que este não é o caso se eu criar a solicitação usando request.post com uma propriedade form explícita.

Eu acho que 2) é irrelevante e o corpo é simplesmente armazenado em outro lugar. Eu estaria interessado em saber se você pode reproduzir 1) embora. Se você enviar a solicitação para localhost:3000 por meio de cURL ( curl --data "some=data" localhost:3000 ), ela termina ou trava?

1) O pedido trava porque no seu exemplo você não está devolvendo nada, usei apenas para ter certeza de que estamos na mesma página. Basta .pipe(res) para receber a resposta.
2) request.body é undefined ao usar a API de fluxo

Na verdade, usei .pipe(res) no meu exemplo real e verifiquei que não funciona. Ele não parece enviar a solicitação e trava. Como seu exemplo funciona, descobri rapidamente a causa do problema:

app.use(bodyParser.urlencoded({ extended: true }));

Parece-me que, quando uso body-parser , a tubulação não funciona mais. Você concorda e, em caso afirmativo, tem ideia de qual pode ser a causa?

Sim, se você der uma olhada no meu primeiro comentário novamente:

// coloque antes de qualquer outro middleware

body-parser processa a solicitação antes de entrar em seu middleware, mas você precisa da solicitação bruta para canalizá-la com sucesso.

Ufa! Ok, obrigado _a ton_ por sua ajuda e paciência. Demorou um pouco para chegar ao fundo disso. Eu ainda acho que é uma chatice que eu não posso canalizar todos os cabeçalhos e outras coisas e depois modificar o corpo, mas acho que não há maneira de contornar isso.

@simov

app.use(function(req, res) {
  req.pipe(
    request[req.method.toLowerCase()]('http://example.com'))
  .pipe(res)
})

irá falhar para DELETE. já que request.del é o método e não req.delete

Eu sei que isso é antigo, mas tive dificuldade em encontrar uma solução adequada para isso e pensei que poderia ser útil para outras pessoas.

'use strict';

let express = require('express');
let request = require('request');

let app = express();

app.use('/', (req, res) => {
  req.pipe(request.post({ url: 'http://www.example.com', form: { foo: 'bar' }}), {end: false}).pipe(res);
});

app.listen(process.env.PORT || 3000);

Conforme mostrado em https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options , adicionar isso faz com que o novo fluxo não seja encerrado automaticamente quando req é encerrado.

Embora isso tenha feito este exemplo funcionar, notei alguns problemas ao criar um proxy para outros métodos usando isso. O corpo de alguma forma faz com que tudo feche adequadamente, mas sem um corpo ele ainda trava. Se estiver usando um analisador de corpo json, isso não deve causar problemas, pois faz com que o corpo seja lido como {} mesmo quando não há corpo, mas sem um ainda pode haver problemas. Minha solução atual é verificar se há um cabeçalho de comprimento de conteúdo na solicitação e usar o objeto de opções, se houver.

@JeffreyAngell tente a sua resolução, ela funciona. valeu

Isso funciona para mim:
`router.put ('/', função (client_req, client_res, next) {
var json = JSON.stringify (client_req.body);
var options = createOptions ('PUT', client_req.baseUrl, json);

var proxy = https.request(options, function (res) {
    client_res.statusCode = res.statusCode;
    res.pipe(client_res, {
        end: true
    });
});

client_req.pipe(proxy, {
    end: true
});

proxy.write(json);
proxy.end();

});

function createOptions (method, targetUrl, json) {
targetUrl = targetUrl.replace ('jira /', '');
console.log ("servindo:" + targetUrl);

const auth = 'Basic ' + new Buffer('usr' + ':' + 'pass').toString('base64');
var headers = {
    'Authorization': auth,
    'Content-Type': 'application/json'
};

return {
    hostname: 'jira.acme.com',
    port: 443,
    path: targetUrl,
    method: method,
    headers: headers
};

} `

@simov

Sim, se você der uma olhada no meu primeiro comentário novamente:

// coloque antes de qualquer outro middleware

body-parser processa a solicitação antes de entrar em seu middleware, mas você precisa da solicitação bruta para canalizá-la com sucesso.

Eu experimentei o mesmo problema. Você poderia explicar por que tem que ser antes de determinado middleware, no meu caso, apenas o middleware bodyParser causa o erro? Parece que posso usar outro middleware, por exemplo, funções de usuário, higienização sem problemas.

EDITAR:
Eu só quero carregar minhas rotas depois que todo o middleware for carregado, então optei por esta solução que exclui bodyParser de todas as rotas com proxy no início:

pathToRegexp = require('path-to-regexp')
exports.excludeMiddleware = (path, middleware) ->
  (req, res, next) ->
    if pathToRegexp(path).test req.path
      next()
    else
      middleware req, res, next
  app.use(routesService.excludeMiddleware '/proxy/(.*)', bodyParser.json({
    extended: false,
    parameterLimit: 10000,
    limit: 1024 * 1024 * 10
  }))

https://stackoverflow.com/questions/27117337/exclude-route-from-express-middleware

Esta página foi útil?
0 / 5 - 0 avaliações