Request: No se puede canalizar desde la solicitud que tiene un cuerpo POST

Creado en 30 jun. 2015  ·  24Comentarios  ·  Fuente: request/request

Estoy intentando crear un nuevo objeto de solicitud canalizando desde una solicitud Express. Esto funciona bien para las solicitudes GET, pero para las solicitudes POST que tienen un cuerpo, el cuerpo no parece estar copiado en la nueva solicitud. Intenté copiar sobre el cuerpo manualmente así:

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

Esto se copia sobre el cuerpo, pero luego aparece un error de "escribir después del final" cuando se limpia pipedReq . Podría estar relacionado con el número 1659.

Es fácil reproducir el problema con la siguiente aplicación Express simple:

'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);

Traza atrás:

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)

Comentario más útil

Sé que esto es antiguo, pero me costó mucho encontrar una solución adecuada y pensé que podría ser útil para otros.

'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);

Como se muestra en https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options , agregar esto hace que la nueva transmisión no se termine automáticamente cuando se termina req.

Si bien esto hizo que este ejemplo funcionara, he notado algunos problemas al crear un proxy para otros métodos que usan esto. El cuerpo de alguna manera hace que cierre todo correctamente, pero sin un cuerpo todavía cuelga. Si usa un analizador de cuerpo json, esto no debería causar un problema, ya que hace que el cuerpo se lea como {} incluso cuando no hay cuerpo, pero sin uno aún podría tener problemas. Mi solución actual es verificar un encabezado de longitud de contenido en la solicitud y usar el objeto de opciones si existe.

Todos 24 comentarios

Prueba con 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')
})

En caso de que desee modificar el cuerpo, simplemente cree una nueva solicitud y canalice a la respuesta.

@simov No quiero modificar el cuerpo, solo la URL. Así que quiero mantener los encabezados y otras propiedades de la solicitud original pero canalizar a una nueva solicitud con una nueva URL. El problema es que el cuerpo no se copia cuando la solicitud original se canaliza a la nueva (no estoy seguro si esto es un error), y si trato de copiar el cuerpo sobre mí mismo especificando la propiedad form en el constructor request , obtengo el error mencionado anteriormente.

Cuando dice "construir una nueva solicitud", ¿está sugiriendo que simplemente cree una nueva solicitud desde cero con el cuerpo que quiero y copie los encabezados de la solicitud original yo mismo, en lugar de insertarlos? Puedo hacer eso, pero sería menos repetitivo y frágil si pudiera usar el soporte de tubería incorporado para copiar la solicitud original en la nueva.

Sigo pensando que uno o ambos de estos dos problemas es probablemente un error (es decir, 1) el cuerpo de la solicitud no se copia al canalizar y 2) la excepción mencionada en mi publicación original).

No sé mucho sobre express o request (solo los uso para un proxy de aplicación de una sola página sin realmente entender cómo funcionan), pero esto solucionó el error write after end para mí:

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

En lugar de:

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

@matthewgertner solo prueba mi ejemplo de código, funciona.

@simov Definitivamente funciona, pero su ejemplo no cubre el caso donde req tiene un cuerpo que quiero copiar. En realidad, estaba usando un código casi idéntico a su ejemplo, pero me quedé atascado cuando tuve que lidiar con las solicitudes POST ya que el cuerpo no se copia en el nuevo objeto de solicitud.

@matthewgertner, ¿puedes darme tu ejemplo de código _exact_ que no funciona?

@simov Es la muestra en mi publicación original. Si utilizo este, el cuerpo no se envía:

'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);

Si uso esto, obtengo esa excepción:

'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);

¿Puedes reemplazar http://www.example.com con alguna URL generada desde http://requestb.in/ y pegar tu enlace aquí? Debería verse algo como esto http://requestb.in/1mv5l8h1?inspect

O también puede usar mi código de ejemplo de arriba, porque eso es lo que estoy usando para server. Entonces estoy ejecutando este script:

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

Como puede ver en mi enlace, tengo el form:{some:'data'} enviado.

Déjame explicarte mi caso de uso. Implementé un proxy API web simple (ver https://github.com/salsita/web-api-proxy). Todo lo que hace es recibir solicitudes HTTP y redirigirlas a otro host, reemplazando los parámetros de URL si es necesario con variables de entorno en el servidor proxy. El punto es evitar tener que incrustar claves secretas en el código del cliente.

Funciona muy bien para solicitudes GET, pero recientemente tuve el requisito de usarlo también para POST. Por lo tanto, no estoy inicializando el cuerpo de mi solicitud como en su comentario anterior, ya que el cuerpo llega a través de la solicitud Express. Tu ejemplo más reciente definitivamente funciona, pero eso realmente no me ayuda. Lo que necesito hacer es:

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

Me parece que si req tiene un cuerpo, no se copia en este caso. Y de todos modos, la verdad es que _no_ necesito modificar el cuerpo (ya que podría tener marcadores de posición que necesito reemplazar usando variables de entorno) así que tendré que hacer:

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

Eso hace que se lance una excepción debido a lo que sospecho que es un error en request .

De todos modos, puedo construir un objeto request completamente nuevo yo mismo, pero quería asegurarme de que no me estaba perdiendo algo y también informar este problema en caso de que sea un error.

No funciona porque siempre está utilizando una solicitud GET . Eche un vistazo a mi ejemplo de código nuevamente.

request[req.method.toLowerCase()]('http://example.com')) establece el método de solicitud según la solicitud entrante.

Además de eso, si necesita modificar el cuerpo, nuevamente desde mi primer comentario:

En caso de que desee modificar el cuerpo, simplemente cree una nueva solicitud y canalice a la respuesta.

No funciona porque siempre está utilizando una solicitud GET. Eche un vistazo a mi ejemplo de código nuevamente.

Pero estoy usando request.post . Eso es lo mismo que request['post'] .

En caso de que desee modificar el cuerpo, simplemente cree una nueva solicitud y canalice a la respuesta.

Es un fastidio que básicamente necesito reescribir el código que ya está en request para canalizar solicitudes en nuevas solicitudes (copiando encabezados, etc.). Puede haber un montón de casos extremos con los que deba ocuparme. Pero sí, supongo que esto es lo que terminaré haciendo.

En mi opinión, debería ser posible canalizar una solicitud a otra solicitud que tenga un cuerpo de formulario sin que se genere una excepción, o ¿cree que hay alguna razón para este comportamiento?

Esto está haciendo una solicitud GET siempre porque no está especificando nada más explícitamente.

La construcción de un nuevo objeto se aplica solo cuando desea modificar el cuerpo.

Seguro, el código base actual no es compatible con POST. Estoy tratando de arreglar eso, que fue cuando encontré los problemas descritos aquí. Solo hice referencia al código base existente para explicar mejor el caso de uso.

Estoy bastante seguro de que siempre necesito crear un nuevo objeto, incluso si no quiero modificar el cuerpo, ya que la operación de tubería no copia el cuerpo. ¿Probaste el ejemplo que proporcioné? Necesita una solicitud Express con un cuerpo que canalice a un objeto request . Verá que el cuerpo es undefined en el nuevo objeto.

GET solicitudes de

Este funciona (tu ejemplo):

'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 esto para hacer la solicitud:

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

some=data se enviaron.

Verá que el cuerpo no está definido en el nuevo objeto.

¿Dónde está eso? No te estoy siguiendo.

Hmmmm está bien. Cuando uso requestb.in (¡buen consejo!) Puedo ver que el cuerpo en realidad _es_ enviado. De hecho, tuve (y todavía tengo) dos problemas que me llevaron a creer que no se estaba enviando:

1) Cuando uso cURL para enviar una solicitud a mi servidor (usando el código en su comentario anterior), se cuelga después de enviar la solicitud (es decir, cURL no termina).
2) Cuando miro request.body es undefined mientras que este no es el caso si creo la solicitud usando request.post con una propiedad form explícita.

Supongo que 2) es irrelevante y el cuerpo simplemente se almacena en otro lugar. Sin embargo, me interesaría saber si puede reproducir 1). Si envía la solicitud a localhost:3000 través de cURL ( curl --data "some=data" localhost:3000 ), ¿termina o se cuelga?

1) La solicitud se cuelga porque en su ejemplo no devuelve nada, la usé solo para asegurarme de que estamos en la misma página. Solo .pipe(res) para recibir la respuesta.
2) request.body es undefined cuando se usa la API de transmisión

De hecho, usé .pipe(res) en mi ejemplo real y verifiqué que no funciona. No parece enviar la solicitud y se cuelga. Como su ejemplo funciona, encontré rápidamente la causa del problema:

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

Me parece que cuando uso body-parser , la tubería ya no funciona. ¿Está de acuerdo y, de ser así, tiene alguna idea de cuál podría ser la causa?

Sí, si vuelves a echar un vistazo a mi primer comentario:

// ponerlo antes que cualquier otro middleware

body-parser procesa la solicitud antes de ingresar a su middleware, pero necesita la solicitud sin procesar para canalizarla correctamente.

¡Uf! Muy bien, muchas gracias por tu ayuda y paciencia. Me tomó un tiempo llegar al fondo de eso. Sigo pensando que es un fastidio que no pueda introducir todos los encabezados y esas cosas y luego modificar el cuerpo, pero supongo que no hay forma de evitarlo.

@simov

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

fallará para DELETE. como request.del es el método y no req.delete

Sé que esto es antiguo, pero me costó mucho encontrar una solución adecuada y pensé que podría ser útil para otros.

'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);

Como se muestra en https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options , agregar esto hace que la nueva transmisión no se termine automáticamente cuando se termina req.

Si bien esto hizo que este ejemplo funcionara, he notado algunos problemas al crear un proxy para otros métodos que usan esto. El cuerpo de alguna manera hace que cierre todo correctamente, pero sin un cuerpo todavía cuelga. Si usa un analizador de cuerpo json, esto no debería causar un problema, ya que hace que el cuerpo se lea como {} incluso cuando no hay cuerpo, pero sin uno aún podría tener problemas. Mi solución actual es verificar un encabezado de longitud de contenido en la solicitud y usar el objeto de opciones si existe.

@JeffreyAngell solo prueba tu resolución, funciona. Gracias

Esto funciona para mi:
`router.put ('/', function (client_req, client_res, next) {
var json = JSON.stringify (client_req.body);
var opciones = 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();

});

función createOptions (método, targetUrl, json) {
targetUrl = targetUrl.replace ('jira /', '');
console.log ("servicio:" + 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

Sí, si vuelves a echar un vistazo a mi primer comentario:

// ponerlo antes que cualquier otro middleware

body-parser procesa la solicitud antes de ingresar a su middleware, pero necesita la solicitud sin procesar para canalizarla correctamente.

Experimenté el mismo problema. ¿Podría explicar por qué tiene que ser antes de cierto middleware en mi caso, solo el middleware bodyParser causa el error? Parece que puedo usar otro middleware, por ejemplo, roles de usuario, desinfectar sin ningún problema.

EDITAR:
Solo quiero cargar mis rutas después de que se cargue todo el middleware, así que opté por esta solución que excluye bodyParser de todas las rutas con proxy al principio:

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

¿Fue útil esta página
0 / 5 - 0 calificaciones