Request: Tidak dapat mem-pipe dari permintaan yang memiliki badan POST

Dibuat pada 30 Jun 2015  ·  24Komentar  ·  Sumber: request/request

Saya mencoba membuat objek permintaan baru dengan menyalurkan dari permintaan Ekspres. Ini berfungsi dengan baik untuk permintaan GET, tetapi untuk permintaan POST yang memiliki isi, isi tampaknya tidak disalin ke dalam permintaan baru. Saya mencoba menyalin seluruh tubuh secara manual seperti:

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

Ini menyalin seluruh tubuh tetapi kemudian saya mendapatkan kesalahan "tulis setelah akhir" ketika pipedReq dibersihkan. Mungkin terkait dengan #1659.

Sangat mudah untuk mereproduksi masalah dengan aplikasi Express sederhana berikut:

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

Jejak mundur:

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)

Komentar yang paling membantu

Saya tahu ini sudah tua, tetapi saya kesulitan menemukan solusi yang cocok untuk ini, dan berpikir itu mungkin berguna bagi orang lain.

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

Seperti yang ditunjukkan di https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options , menambahkan ini membuatnya jadi aliran baru tidak otomatis dihentikan ketika req dihentikan.

Meskipun ini menyebabkan contoh ini berfungsi, saya perhatikan beberapa masalah membuat proxy untuk metode lain menggunakan ini. Tubuh entah bagaimana menyebabkannya menutup semuanya dengan benar, tetapi tanpa tubuh itu masih menggantung. Jika menggunakan parser badan json, ini seharusnya tidak menimbulkan masalah karena menyebabkan badan dibaca sebagai {} bahkan ketika tidak ada badan, tetapi tanpa badan itu masih bisa bermasalah. Solusi saya saat ini adalah memeriksa tajuk panjang konten pada permintaan, dan menggunakan objek opsi jika ada.

Semua 24 komentar

Coba dengan yang ini:

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

Jika Anda ingin mengubah isi, cukup buat permintaan baru dan pipa ke respons.

@simov Saya tidak ingin mengubah isi, hanya URL. Jadi saya ingin menyimpan header dan properti lain dari permintaan asli tetapi menyalurkan ke permintaan baru dengan URL baru. Masalahnya adalah bahwa tubuh tidak disalin ketika permintaan asli disalurkan ke yang baru (tidak yakin apakah ini bug), dan jika saya mencoba menyalin tubuh sendiri dengan menentukan properti form di konstruktor request , saya mendapatkan kesalahan yang disebutkan di atas.

Ketika Anda mengatakan "buat permintaan baru", apakah Anda menyarankan agar saya membuat permintaan baru dari awal dengan badan yang saya inginkan dan menyalin header dari permintaan asli sendiri, daripada menyalurkannya? Saya bisa melakukan itu tetapi akan kurang berulang dan rapuh jika saya bisa menggunakan dukungan pipa bawaan untuk menyalin permintaan asli di yang baru.

Saya masih berpikir salah satu atau kedua masalah ini mungkin merupakan bug (yaitu 1) badan permintaan tidak disalin saat pemipaan dan 2) pengecualian yang disebutkan dalam posting originak saya).

Saya tidak tahu terlalu banyak tentang ekspres atau permintaan (hanya menggunakan ini untuk proxy aplikasi satu halaman tanpa benar-benar memahami cara kerjanya), tetapi ini memperbaiki kesalahan write after end untuk saya:

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

Dari pada:

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

@matthewgertner coba saja contoh kode saya, itu berhasil.

@simov Ini pasti berfungsi, tetapi contoh Anda tidak mencakup kasus di mana req memiliki badan yang ingin saya salin. Saya sebenarnya menggunakan kode yang hampir identik dengan contoh Anda, tetapi saya terjebak ketika saya harus berurusan dengan permintaan POST karena badan tidak disalin ke objek permintaan baru.

@matthewgertner dapatkah Anda memberi saya contoh kode _exact_ Anda yang tidak berfungsi?

@simov Ini sampel di posting asli saya. Jika saya menggunakan yang ini maka tubuh tidak dikirim:

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

Jika saya menggunakan ini maka saya mendapatkan pengecualian itu:

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

Bisakah Anda mengganti http://www.example.com dengan beberapa URL yang dihasilkan dari http://requestb.in/ dan menempelkan tautan Anda di sini? Seharusnya terlihat seperti ini http://requestb.in/1mv5l8h1?inspect

Atau Anda dapat menggunakan kode contoh saya dari atas juga, karena itulah yang saya gunakan untuk server. Lalu saya menjalankan skrip ini:

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

Seperti yang Anda lihat di tautan saya, saya telah mengirim form:{some:'data'} .

Biarkan saya menjelaskan kasus penggunaan saya. Saya menerapkan proxy API web sederhana (lihat https://github.com/salsita/web-api-proxy). Yang dilakukannya hanyalah menerima permintaan HTTP dan mengarahkannya ke host lain, mengganti parameter URL jika perlu dengan variabel lingkungan di server proxy. Intinya adalah untuk menghindari keharusan menyematkan kunci rahasia ke dalam kode klien.

Ini berfungsi dengan baik untuk permintaan GET, tetapi saya baru-baru ini memiliki persyaratan untuk menggunakannya untuk POST juga. Jadi saya tidak menginisialisasi isi permintaan saya seperti pada komentar Anda sebelumnya karena isi datang melalui permintaan Ekspres. Contoh terbaru Anda pasti berfungsi tetapi itu tidak terlalu membantu saya. Yang perlu saya lakukan adalah:

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

Sepertinya saya bahwa jika req memiliki tubuh, itu tidak akan disalin dalam kasus ini. Lagi pula, kenyataannya adalah saya _do_ perlu memodifikasi tubuh (karena mungkin ada penampung yang perlu saya ganti menggunakan variabel lingkungan) jadi saya harus melakukan:

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

Itu menyebabkan pengecualian dilemparkan karena apa yang saya curigai adalah bug di request .

Bagaimanapun, saya dapat membuat objek request benar-benar baru sendiri, tetapi saya ingin memastikan bahwa saya tidak melewatkan sesuatu dan juga melaporkan masalah ini jika itu adalah bug.

Itu tidak berhasil karena Anda selalu menggunakan permintaan GET . Lihatlah contoh kode saya lagi.

request[req.method.toLowerCase()]('http://example.com')) menetapkan metode permintaan berdasarkan permintaan yang masuk.

Selain itu jika Anda perlu memodifikasi tubuh, sekali lagi dari komentar pertama saya:

Jika Anda ingin mengubah isi, cukup buat permintaan baru dan pipa ke respons.

Itu tidak berhasil karena Anda selalu menggunakan permintaan GET. Lihatlah contoh kode saya lagi.

Tapi saya menggunakan request.post . Itu sama dengan request['post'] .

Jika Anda ingin mengubah isi, cukup buat permintaan baru dan pipa ke respons.

Sangat disayangkan bahwa pada dasarnya saya perlu menulis ulang kode yang sudah ada di request untuk menyalurkan permintaan ke permintaan baru (menyalin header, dll.). Mungkin ada banyak kasus tepi yang harus saya tangani. Tapi ya, saya kira inilah yang akan saya lakukan pada akhirnya.

IMO seharusnya dimungkinkan untuk menyalurkan permintaan ke permintaan lain yang memiliki badan formulir tanpa pengecualian yang dilemparkan, atau menurut Anda ada beberapa alasan untuk perilaku ini?

Ini membuat permintaan GET selalu karena Anda tidak menentukan hal lain secara eksplisit.

Pembuatan objek baru hanya berlaku bila Anda ingin memodifikasi badan.

Tentu, basis kode saat ini tidak mendukung POST. Saya mencoba memperbaikinya, saat itulah saya mengalami masalah yang diuraikan di sini. Saya hanya mereferensikan basis kode yang ada untuk menjelaskan kasus penggunaan dengan lebih baik.

Saya cukup yakin saya harus selalu membuat objek baru, bahkan jika saya tidak ingin memodifikasi tubuh, karena tubuh tidak disalin oleh operasi pipa. Apakah Anda mencoba contoh yang saya berikan? Anda memerlukan permintaan Ekspres dengan badan yang Anda pipa ke objek request . Anda akan melihat bahwa tubuh adalah undefined di objek baru.

GET permintaan tidak memiliki badan, itu berdasarkan desain.

Yang ini berfungsi (contoh Anda):

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

Menggunakan ini untuk membuat permintaan:

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

some=data dikirim.

Anda akan melihat bahwa tubuh tidak terdefinisi di objek baru.

Dimana itu, aku tidak mengikutimu.

Hmm, oke. Ketika saya menggunakan requestb.in (tip yang bagus!) Saya dapat melihat bahwa isi sebenarnya _is_ terkirim. Saya sebenarnya memiliki (dan masih memiliki) dua masalah yang membuat saya percaya bahwa itu tidak dikirim:

1) Ketika saya menggunakan cURL untuk mengirim permintaan ke server saya (menggunakan kode di komentar Anda sebelumnya), itu hang setelah mengirim permintaan (yaitu cURL tidak berhenti).
2) Ketika saya melihat request.body itu adalah undefined sedangkan ini tidak terjadi jika saya membuat permintaan menggunakan request.post dengan properti form eksplisit.

Saya kira 2) tidak relevan dan tubuh hanya disimpan di tempat lain. Saya tertarik untuk mengetahui apakah Anda dapat mereproduksi 1). Jika Anda mengirim permintaan ke localhost:3000 melalui cURL ( curl --data "some=data" localhost:3000 ), apakah itu berhenti atau hang?

1) Permintaan hang karena dalam contoh Anda, Anda tidak mengembalikan apa pun, saya menggunakannya hanya untuk memastikan kami berada di halaman yang sama. Cukup .pipe(res) untuk menerima tanggapan.
2) request.body adalah undefined saat menggunakan API aliran

Saya sebenarnya menggunakan .pipe(res) dalam contoh nyata saya dan saya memverifikasi bahwa itu tidak berfungsi. Tampaknya tidak mengirim permintaan dan hang. Karena contoh Anda berfungsi, saya segera menemukan penyebab masalahnya:

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

Sepertinya saya ketika saya menggunakan body-parser , perpipaan tidak lagi berfungsi. Apakah Anda setuju dan, jika ya, apakah Anda tahu apa penyebabnya?

Yap, jika Anda melihat kembali komentar pertama saya:

// letakkan sebelum middleware lainnya

body-parser memproses permintaan sebelum memasuki middleware Anda, tetapi Anda memerlukan permintaan mentah untuk menyalurkannya dengan sukses.

Fiuh! Oke, terima kasih _a ton_ atas bantuan dan kesabaran Anda. Butuh beberapa saat untuk sampai ke dasar itu. Saya masih berpikir itu agak menyedihkan bahwa saya tidak bisa menyalurkan semua header dan hal-hal dan kemudian memodifikasi tubuh, tapi saya kira tidak ada jalan lain.

@simov

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

akan gagal untuk DELETE. karena request.del adalah metodenya dan bukan req.delete

Saya tahu ini sudah tua, tetapi saya kesulitan menemukan solusi yang cocok untuk ini, dan berpikir itu mungkin berguna bagi orang lain.

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

Seperti yang ditunjukkan di https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options , menambahkan ini membuatnya jadi aliran baru tidak otomatis dihentikan ketika req dihentikan.

Meskipun ini menyebabkan contoh ini berfungsi, saya perhatikan beberapa masalah membuat proxy untuk metode lain menggunakan ini. Tubuh entah bagaimana menyebabkannya menutup semuanya dengan benar, tetapi tanpa tubuh itu masih menggantung. Jika menggunakan parser badan json, ini seharusnya tidak menimbulkan masalah karena menyebabkan badan dibaca sebagai {} bahkan ketika tidak ada badan, tetapi tanpa badan itu masih bisa bermasalah. Solusi saya saat ini adalah memeriksa tajuk panjang konten pada permintaan, dan menggunakan objek opsi jika ada.

@JeffreyAngell coba saja resolusi Anda, itu berhasil. Terima kasih

Ini bekerja untuk saya:
`router.put('/', fungsi (klien_req, klien_res, selanjutnya) {
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();

});

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

Yap, jika Anda melihat kembali komentar pertama saya:

// letakkan sebelum middleware lainnya

body-parser memproses permintaan sebelum memasuki middleware Anda, tetapi Anda memerlukan permintaan mentah untuk menyalurkannya dengan sukses.

Saya mengalami masalah yang sama. Bisakah Anda menjelaskan mengapa harus sebelum middleware tertentu dalam kasus saya hanya middleware bodyParser menyebabkan kesalahan? Sepertinya saya bisa menggunakan middleware lain misalnya peran pengguna, sanitasi tanpa masalah.

EDIT:
Saya hanya ingin memuat rute saya setelah semua middleware dimuat, jadi saya menggunakan solusi ini yang mengecualikan bodyParser dari semua rute dengan proxy di awal:

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

Apakah halaman ini membantu?
0 / 5 - 0 peringkat