Request: Von einer Anfrage mit einem POST-Text kann keine Weiterleitung durchgeführt werden

Erstellt am 30. Juni 2015  ·  24Kommentare  ·  Quelle: request/request

Ich versuche, ein neues Anforderungsobjekt zu erstellen, indem ich von einer Express-Anforderung aus weiterleite. Dies funktioniert gut für GET-Anforderungen, aber für POST-Anforderungen mit einem Hauptteil scheint der Hauptteil nicht in die neue Anforderung kopiert zu werden. Ich habe versucht, den Körper manuell wie folgt zu kopieren:

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

Dies kopiert den Hauptteil, aber dann erhalte ich einen Fehler "Schreiben nach Ende", wenn pipedReq bereinigt wird. Könnte mit #1659 zusammenhängen.

Mit der folgenden einfachen Express-App lässt sich das Problem leicht reproduzieren:

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

Rückverfolgung:

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)

Hilfreichster Kommentar

Ich weiß, dass dies alt ist, aber es fiel mir schwer, eine geeignete Lösung dafür zu finden, und dachte, dass es für andere nützlich sein könnte.

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

Wie in https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options gezeigt , bewirkt das Hinzufügen, dass der neue Stream nicht automatisch beendet wird, wenn req beendet wird.

Während dies dazu führte, dass dieses Beispiel funktionierte, habe ich einige Probleme festgestellt, die einen Proxy für andere Methoden verwenden, die dies verwenden. Der Körper bewirkt irgendwie, dass es alles richtig schließt, aber ohne Körper hängt es immer noch. Wenn Sie einen Json-Body-Parser verwenden, sollte dies kein Problem darstellen, da der Text selbst dann als {} gelesen wird, wenn kein Body vorhanden ist, aber ohne einen kann es immer noch Probleme geben. Meine aktuelle Lösung besteht darin, in der Anforderung nach einem Header mit Inhaltslänge zu suchen und das Optionsobjekt zu verwenden, falls eines vorhanden ist.

Alle 24 Kommentare

Versuchen Sie es mit diesem:

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

Falls Sie den Textkörper ändern möchten, erstellen Sie einfach eine neue Anfrage und leiten Sie die Antwort an die Pipe weiter.

@simov Ich möchte den Körper nicht ändern, nur die URL. Ich möchte also die Header und andere Eigenschaften der ursprünglichen Anfrage beibehalten, aber in eine neue Anfrage mit einer neuen URL weiterleiten. Das Problem ist, dass der Körper nicht kopiert wird, wenn die ursprüngliche Anfrage in die neue weitergeleitet wird (nicht sicher, ob dies ein Fehler ist) und wenn ich versuche, den Körper über mich selbst zu kopieren, indem ich die Eigenschaft form angebe im request Konstruktor erhalte ich den oben genannten Fehler.

Wenn Sie sagen "neue Anfrage erstellen", schlagen Sie vor, dass ich einfach eine neue Anfrage mit dem gewünschten Textkörper erstelle und die Header der ursprünglichen Anfrage selbst überschreibe, anstatt sie einzufügen? Ich kann das tun, aber es wäre weniger repetitiv und zerbrechlich, wenn ich die eingebaute Rohrstütze verwenden könnte, um die ursprüngliche Anfrage in die neue zu kopieren.

Ich denke immer noch, dass eines oder beide dieser beiden Probleme wahrscheinlich ein Fehler sind (dh 1) der Anforderungskörper wird beim Versenden nicht kopiert und 2) die in meinem ursprünglichen Beitrag erwähnte Ausnahme).

Ich weiß nicht viel über Express oder Request (nur diese für einen einseitigen App-Proxy zu verwenden, ohne wirklich zu verstehen, wie sie funktionieren), aber das hat den write after end Fehler für mich behoben:

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

Anstatt von:

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

@matthewgertner probiere einfach mein Codebeispiel aus, es funktioniert.

@simov Es funktioniert definitiv, aber Ihr Beispiel deckt nicht den Fall ab, in dem req einen Körper hat, den ich kopieren möchte. Ich habe tatsächlich Code verwendet, der fast identisch mit Ihrem Beispiel ist, aber ich bin stecken geblieben, als ich mit POST-Anfragen umgehen musste, da der Text nicht in das neue Anforderungsobjekt kopiert wird.

@matthewgertner kannst du mir dein _exaktes_ Codebeispiel geben, das nicht funktioniert?

@simov Es ist das Beispiel in meinem ursprünglichen Beitrag. Wenn ich diesen verwende, wird der Körper nicht gesendet:

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

Wenn ich das verwende, bekomme ich diese Ausnahme:

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

Können Sie http://www.example.com durch eine generierte URL von http://requestb.in/ ersetzen und Ihren Link hier einfügen? Es sollte ungefähr so ​​aussehen http://requestb.in/1mv5l8h1?inspect

Oder Sie können auch meinen Beispielcode von oben verwenden, da ich ihn für den Server verwende. Dann führe ich dieses Skript aus:

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

Wie Sie in meinem Link sehen können, habe ich die form:{some:'data'} gesendet.

Lassen Sie mich meinen Anwendungsfall erklären. Ich habe einen einfachen Web-API-Proxy implementiert (siehe https://github.com/salsita/web-api-proxy). Es empfängt lediglich HTTP-Anfragen und leitet sie an einen anderen Host weiter, wobei URL-Parameter bei Bedarf durch Umgebungsvariablen auf dem Proxy-Server ersetzt werden. Der Punkt besteht darin, zu vermeiden, dass geheime Schlüssel in den Clientcode eingebettet werden müssen.

Es funktioniert hervorragend für GET-Anfragen, aber ich hatte vor kurzem die Anforderung, es auch für POST zu verwenden. Daher initialisiere ich den Text meiner Anfrage nicht wie in Ihrem vorherigen Kommentar, da der Text über die Express-Anfrage kommt. Dein neuestes Beispiel funktioniert definitiv, aber das hilft mir nicht wirklich weiter. Was ich tun muss ist:

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

Es scheint mir, dass, wenn req einen Körper hat, dieser in diesem Fall nicht kopiert wird. Und wie auch immer, die Wahrheit ist, dass ich den Körper _do_ ändern muss (da er möglicherweise Platzhalter enthält, die ich mit Umgebungsvariablen ersetzen muss), also muss ich Folgendes tun:

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

Das führt dazu, dass eine Ausnahme ausgelöst wird, weil ich vermute, dass es sich um einen Fehler in request .

Wie auch immer, ich kann selbst ein komplett neues request Objekt bauen, aber ich wollte sicher gehen, dass ich nichts übersehe und auch dieses Problem melden, falls es sich um einen Fehler handelt.

Es funktioniert nicht, weil Sie immer eine GET Anfrage verwenden. Schauen Sie sich noch einmal mein Codebeispiel an.

request[req.method.toLowerCase()]('http://example.com')) legt die Anfragemethode basierend auf der eingehenden Anfrage fest.

Abgesehen davon, wenn Sie den Körper ändern müssen, noch einmal aus meinem ersten Kommentar:

Falls Sie den Textkörper ändern möchten, erstellen Sie einfach eine neue Anfrage und leiten Sie die Antwort an die Pipe weiter.

Es funktioniert nicht, weil Sie immer eine GET-Anfrage verwenden. Schauen Sie sich noch einmal mein Codebeispiel an.

Aber ich benutze request.post . Das ist dasselbe wie request['post'] .

Falls Sie den Textkörper ändern möchten, erstellen Sie einfach eine neue Anfrage und leiten Sie die Antwort an die Pipe weiter.

Es ist schade, dass ich im Grunde genommen den Code, der bereits in request , neu schreiben muss, um Anfragen in neue Anfragen umzuleiten (Kopieren von Headern usw.). Es kann eine Reihe von Randfällen geben, mit denen ich mich befassen muss. Aber ja, ich denke, das werde ich am Ende tun.

IMO sollte es möglich sein, eine Anfrage in eine andere Anfrage mit einem Formularkörper umzuleiten, ohne dass eine Ausnahme ausgelöst wird, oder glauben Sie, dass es einen Grund für dieses Verhalten gibt?

Dies ist eine GET Anfrage immer, weil Sie nichts anderes explizit angeben.

Das Erstellen eines neuen Objekts gilt nur, wenn Sie den Körper ändern möchten.

Sicher, die aktuelle Codebasis unterstützt POST nicht. Ich versuche, das zu beheben, und bin dabei auf die hier beschriebenen Probleme gestoßen. Ich habe nur auf die vorhandene Codebasis verwiesen, um den Anwendungsfall besser zu erklären.

Ich bin mir ziemlich sicher, dass ich immer ein neues Objekt erstellen muss, auch wenn ich den Körper nicht ändern möchte, da der Körper durch die Pipe-Operation nicht kopiert wird. Hast du das von mir angegebene Beispiel ausprobiert? Sie benötigen eine Express-Anfrage mit einem Body, den Sie in ein request Objekt umleiten. Sie werden sehen, dass der Körper im neuen Objekt undefined .

GET Anfragen haben keinen Körper, das ist beabsichtigt.

Dieser funktioniert (Ihr Beispiel):

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

Verwenden Sie dies, um die Anfrage zu stellen:

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

some=data wurde gesendet.

Sie werden sehen, dass der Körper im neuen Objekt nicht definiert ist.

Wo ist das, ich folge dir nicht.

Hmmm okay. Wenn ich requestb.in benutze (netter Tipp!) kann ich sehen, dass der Body tatsächlich _gesendet_ wird. Ich hatte (und habe) tatsächlich zwei Probleme, die mich glauben ließen, dass es nicht gesendet wurde:

1) Wenn ich cURL verwende, um eine Anfrage an meinen Server zu senden (mit dem Code in Ihrem vorherigen Kommentar), hängt es nach dem Senden der Anfrage (dh cURL wird nicht beendet).
2) Wenn ich request.body , ist es undefined während dies nicht der Fall ist, wenn ich die Anfrage mit request.post mit einer expliziten form Eigenschaft erstelle.

Ich vermute, dass 2) irrelevant ist und der Körper einfach woanders gelagert wird. Mich würde interessieren, ob Sie 1) reproduzieren können. Wenn Sie die Anfrage per cURL ( curl --data "some=data" localhost:3000 ) an localhost:3000 senden, wird sie beendet oder hängt sie?

1) Die Anfrage hängt, weil Sie in Ihrem Beispiel nichts zurückgeben. Ich habe sie nur verwendet, um sicherzustellen, dass wir auf derselben Seite sind. Nur .pipe(res) , um die Antwort zu erhalten.
2) request.body ist undefined bei Verwendung der Stream-API

Ich habe tatsächlich .pipe(res) in meinem echten Beispiel verwendet und überprüft, dass es nicht funktioniert. Es scheint die Anfrage nicht zu senden und es hängt. Da dein Beispiel funktioniert, habe ich schnell die Ursache des Problems gefunden:

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

Es sieht für mich so aus, als ob die Verrohrung nicht mehr funktioniert, wenn ich body-parser . Stimmen Sie zu und wenn ja, haben Sie eine Idee, was die Ursache sein könnte?

Ja, wenn du dir noch einmal meinen ersten Kommentar ansiehst:

// vor jeder anderen Middleware setzen

body-parser verarbeitet die Anforderung, bevor Sie Ihre Middleware eingeben, aber Sie benötigen die Rohanforderung, um sie erfolgreich weiterzuleiten.

Puh! Okay, vielen Dank für deine Hilfe und Geduld. Es hat eine Weile gedauert, dem auf den Grund zu gehen. Ich denke immer noch, dass es irgendwie schade ist, dass ich nicht alle Header und so weiterleiten und dann den Körper modifizieren kann, aber ich denke, daran führt kein Weg vorbei.

@simov

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

wird für DELETE fehlschlagen. da request.del die Methode ist und nicht req.delete

Ich weiß, dass dies alt ist, aber es fiel mir schwer, eine geeignete Lösung dafür zu finden, und dachte, dass es für andere nützlich sein könnte.

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

Wie in https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options gezeigt , bewirkt das Hinzufügen, dass der neue Stream nicht automatisch beendet wird, wenn req beendet wird.

Während dies dazu führte, dass dieses Beispiel funktionierte, habe ich einige Probleme festgestellt, die einen Proxy für andere Methoden verwenden, die dies verwenden. Der Körper bewirkt irgendwie, dass es alles richtig schließt, aber ohne Körper hängt es immer noch. Wenn Sie einen Json-Body-Parser verwenden, sollte dies kein Problem darstellen, da der Text selbst dann als {} gelesen wird, wenn kein Body vorhanden ist, aber ohne einen kann es immer noch Probleme geben. Meine aktuelle Lösung besteht darin, in der Anforderung nach einem Header mit Inhaltslänge zu suchen und das Optionsobjekt zu verwenden, falls eines vorhanden ist.

@JeffreyAngell probiere einfach deine Auflösung aus, es funktioniert. Danke

Das funktioniert bei mir:
`router.put('/', function (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();

});

Funktion createOptions(method, targetUrl, json) {
targetUrl = targetUrl.replace('jira/', '');
console.log("serving: " + 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

Ja, wenn du dir noch einmal meinen ersten Kommentar ansiehst:

// vor jeder anderen Middleware setzen

body-parser verarbeitet die Anforderung, bevor Sie Ihre Middleware eingeben, aber Sie benötigen die unformatierte Anforderung, um sie erfolgreich weiterzuleiten.

Ich habe das gleiche Problem erlebt. Könnten Sie erklären, warum es in meinem Fall vor einer bestimmten Middleware sein muss, dass nur die bodyParser Middleware den Fehler verursacht? Es scheint, dass ich andere Middleware, zB Benutzerrollen, verwenden kann, ohne Probleme zu bereinigen.

BEARBEITEN:
Ich möchte meine Routen nur laden, nachdem die gesamte Middleware geladen ist, also habe ich mich für diese Lösung entschieden, die bodyParser von allen Routen mit proxy am Anfang ausschließt:

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

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen