Request: لا يمكن توجيه الطلب الذي يحتوي على نص POST

تم إنشاؤها على ٣٠ يونيو ٢٠١٥  ·  24تعليقات  ·  مصدر: request/request

أحاول إنشاء كائن طلب جديد عن طريق التوصيل من طلب سريع. يعمل هذا بشكل جيد مع طلبات GET ، ولكن بالنسبة لطلبات POST التي لها جسم ، لا يبدو أن النص منسوخًا في الطلب الجديد. حاولت نسخ الجسم يدويًا كالتالي:

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

يتم نسخ هذه النسخ على الجسم ولكن بعد ذلك أحصل على خطأ "الكتابة بعد النهاية" عند تنظيف pipedReq . قد تكون مرتبطة بـ # 1659.

من السهل إعادة إنتاج المشكلة باستخدام تطبيق Express البسيط التالي:

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

التعليق الأكثر فائدة

أعلم أن هذا قديم ، لكنني واجهت صعوبة في إيجاد حل مناسب لذلك ، واعتقدت أنه قد يكون مفيدًا للآخرين.

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

كما هو موضح في https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options ، فإن إضافة هذا تجعله لا يتم إنهاء الدفق الجديد تلقائيًا عند إنهاء req.

بينما تسبب هذا في عمل هذا المثال ، لاحظت بعض المشكلات في إنشاء وكيل لطرق أخرى باستخدام هذا. يتسبب الجسم بطريقة ما في إغلاق كل شيء بشكل صحيح ، ولكن بدون وجود جسم لا يزال معلقًا. في حالة استخدام محلل json body ، لا ينبغي أن يتسبب ذلك في حدوث مشكلة لأنه يتسبب في قراءة الجسم على أنه {} حتى في حالة عدم وجود أحد ، ولكن بدون أحد ، لا يزال من الممكن أن يواجه مشكلات. الحل الحالي الخاص بي هو التحقق من وجود عنوان طول المحتوى في الطلب ، واستخدام كائن الخيارات إذا كان موجودًا.

ال 24 كومينتر

جرب مع هذا:

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

في حالة رغبتك في تعديل الجسم ، ما عليك سوى إنشاء طلب جديد وتوجيه الاستجابة.

simov لا أريد تعديل النص ، فقط عنوان URL. لذلك أريد الاحتفاظ بالرؤوس والخصائص الأخرى للطلب الأصلي ولكن مع توجيه طلب جديد بعنوان URL جديد. تكمن المشكلة في أنه لا يتم نسخ الجسم عند إرسال الطلب الأصلي إلى الطلب الجديد (لست متأكدًا مما إذا كان هذا خطأ) ، وإذا حاولت نسخ النص فوقي عن طريق تحديد الخاصية form في المُنشئ request ، يظهر لي الخطأ المذكور أعلاه.

عندما تقول "أنشئ طلبًا جديدًا" ، هل تقترح أن أقوم ببساطة بإنشاء طلب جديد من نقطة الصفر بالجسم الذي أريده ونسخ الرؤوس من الطلب الأصلي بنفسي ، بدلاً من إدخالها؟ يمكنني القيام بذلك ولكن سيكون أقل تكرارًا وهشًا إذا كان بإمكاني استخدام دعم الأنبوب المدمج لنسخ الطلب الأصلي في الطلب الجديد.

ما زلت أعتقد أن إحدى هاتين المسألتين أو كلتيهما هي على الأرجح خطأ (أي 1) لا يتم نسخ نص الطلب عند الأنابيب و 2) الاستثناء المذكور في منشور Origak الخاص بي).

لا أعرف الكثير عن التعبير السريع أو الطلب (استخدم فقط هذه مع وكيل تطبيق أحادي الصفحة دون فهم كيفية عملها حقًا) ، ولكن هذا أصلح الخطأ write after end بالنسبة لي:

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

بدلا من:

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

matthewgertner فقط جرب مثال الكود الخاص بي ، إنه يعمل.

simov إنه يعمل بالتأكيد ، لكن req على جسم أريد نسخه. كنت في الواقع أستخدم رمزًا مطابقًا تقريبًا لمثالك ، لكنني تعثرت عندما اضطررت للتعامل مع طلبات POST نظرًا لعدم نسخ النص إلى كائن الطلب الجديد.

matthewgertner هل يمكنك أن تعطيني مثال كود_exact_ الذي لا يعمل؟

simov إنها العينة في رسالتي الأصلية. إذا استخدمت هذا ، فلن يتم إرسال الجسد:

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

إذا استخدمت هذا ، فسأحصل على هذا الاستثناء:

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

هل يمكنك استبدال http://www.example.com ببعض عناوين URL التي تم إنشاؤها من http://requestb.in/ ولصق الرابط الخاص بك هنا؟ يجب أن يبدو مثل هذا http://requestb.in/1mv5l8h1؟inspect

أو يمكنك استخدام رمز المثال الخاص بي من الأعلى أيضًا ، لأن هذا هو ما أستخدمه للخادم. ثم أقوم بتنفيذ هذا النص البرمجي:

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

كما ترى في الارتباط الخاص بي ، لقد تم إرسال form:{some:'data'} .

اسمحوا لي أن أشرح حالة الاستخدام الخاصة بي. لقد قمت بتطبيق وكيل بسيط لواجهة برمجة تطبيقات الويب (راجع https://github.com/salsita/web-api-proxy). كل ما يفعله هو تلقي طلبات HTTP وإعادة توجيهها إلى مضيف آخر ، واستبدال معلمات URL إذا لزم الأمر بمتغيرات البيئة على الخادم الوكيل. الهدف هو تجنب الاضطرار إلى تضمين المفاتيح السرية في رمز العميل.

إنه يعمل بشكل رائع مع طلبات GET ، ولكن كان لدي مؤخرًا متطلب لاستخدامه في POST أيضًا. لذلك أنا لا أقوم بتهيئة نص طلبي كما في تعليقك السابق لأن الجسم يأتي من خلال الطلب السريع. أحدث مثال لك يعمل بالتأكيد ولكن هذا لا يساعدني حقًا. ما علي فعله هو:

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

يبدو لي أنه إذا كان لدى req جسم ، فلن يتم نسخه في هذه الحالة. وعلى أي حال ، الحقيقة هي أنني أحتاج إلى تعديل الجسم (لأنه قد يحتوي على عناصر نائبة أحتاج إلى استبدالها باستخدام متغيرات البيئة) لذلك سأفعل:

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

يؤدي ذلك إلى طرح استثناء بسبب ما أشتبه في أنه خطأ في request .

على أي حال ، يمكنني إنشاء كائن request جديد تمامًا بنفسي ، لكنني أردت التأكد من أنني لم أفقد شيئًا وأيضًا للإبلاغ عن هذه المشكلة في حالة وجود خطأ.

لا يعمل لأنك تستخدم طلب GET دائمًا. ألق نظرة على مثال الكود الخاص بي مرة أخرى.

يعين request[req.method.toLowerCase()]('http://example.com')) طريقة الطلب بناءً على الطلب الوارد.

بالإضافة إلى ذلك ، إذا كنت بحاجة إلى تعديل النص ، مرة أخرى من تعليقي الأول:

في حالة رغبتك في تعديل الجسم ، ما عليك سوى إنشاء طلب جديد وتوجيه الاستجابة.

لا يعمل لأنك تستخدم طلب GET دائمًا. ألق نظرة على مثال الكود الخاص بي مرة أخرى.

لكني أستخدم request.post . هذا هو نفس الشيء مثل request['post'] .

في حالة رغبتك في تعديل الجسم ، ما عليك سوى إنشاء طلب جديد وتوجيه الاستجابة.

من المشكل أن أحتاج إلى إعادة كتابة الكود الموجود بالفعل في request لتوجيه الطلبات إلى الطلبات الجديدة (نسخ الرؤوس ، وما إلى ذلك). قد تكون هناك مجموعة من حالات الحافة التي أحتاج إلى التعامل معها. لكن نعم ، أعتقد أن هذا ما سأفعله في النهاية.

ينبغي أن يكون من الممكن توجيه طلب إلى IMO في طلب آخر يحتوي على نموذج نصي دون استثناء ، أو هل تعتقد أن هناك سببًا لهذا السلوك؟

يؤدي هذا إلى تقديم طلب GET دائمًا لأنك لا تحدد أي شيء آخر بشكل صريح.

يتم تطبيق إنشاء كائن جديد فقط عندما تريد تعديل الجسم.

بالتأكيد ، قاعدة البيانات الحالية لا تدعم POST. أحاول إصلاح ذلك ، عندما واجهت المشكلات الموضحة هنا. لقد أشرت فقط إلى مصدر البرنامج الحالي لشرح حالة الاستخدام بشكل أفضل.

أنا متأكد من أنني بحاجة إلى إنشاء كائن جديد دائمًا ، حتى لو لم أرغب في تعديل الجسم ، نظرًا لأن الجسم لا يتم نسخه بواسطة عملية الأنبوب. هل جربت المثال الذي قدمته؟ أنت بحاجة إلى طلب سريع بجسم تقوم بإدخاله في كائن request . ستلاحظ أن الجسم هو undefined في الكائن الجديد.

طلبات GET ليس لها جسم ، وهذا حسب التصميم.

هذا واحد يعمل (مثالك):

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

باستخدام هذا لتقديم الطلب:

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

some=data .

سترى أن الجسم غير محدد في الكائن الجديد.

أين هذا ، أنا لا أتبعك.

امممم حسنا. عندما أستخدم requestb.in (نصيحة لطيفة!) أستطيع أن أرى أن الجثة _is_ أرسلت بالفعل. لدي بالفعل (وما زلت) مشكلتان دفعتني إلى الاعتقاد بأنه لم يتم إرساله:

1) عندما أستخدم cURL لإرسال طلب إلى الخادم الخاص بي (باستخدام الرمز الموجود في تعليقك السابق) ، فإنه يتوقف بعد إرسال الطلب (أي أن cURL لا ينتهي).
2) عندما أنظر إلى request.body يكون الأمر undefined بينما هذا ليس هو الحال إذا قمت بإنشاء الطلب باستخدام request.post مع خاصية form صريحة.

أعتقد أن 2) غير ذي صلة والجسد يتم تخزينه ببساطة في مكان آخر. سأكون مهتمًا بمعرفة ما إذا كان يمكنك إعادة إنتاج 1) رغم ذلك. إذا أرسلت الطلب إلى localhost:3000 عبر cURL ( curl --data "some=data" localhost:3000 ) ، فهل ينتهي أم يتعطل؟

1) تم تعليق الطلب لأنك في مثالك لا تعيد أي شيء ، لقد استخدمته فقط للتأكد من أننا في نفس الصفحة. فقط .pipe(res) لتلقي الرد.
2) request.body هو undefined عند استخدام واجهة برمجة التطبيقات للدفق

لقد استخدمت بالفعل .pipe(res) في المثال الحقيقي الخاص بي وتحققت من أنه لا يعمل. لا يبدو أنه يرسل الطلب ويتوقف. نظرًا لأن مثالك يعمل ، وجدت بسرعة سبب المشكلة:

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

يبدو لي أنه عندما أستخدم body-parser ، فإن الأنابيب لم تعد تعمل. هل توافق ، وإذا كان الأمر كذلك ، فهل لديك أي فكرة عن السبب المحتمل؟

نعم ، إذا ألقيت نظرة على تعليقي الأول مرة أخرى:

// ضعها قبل أي برمجيات وسيطة أخرى

يعالج body-parser الطلب قبل إدخال البرنامج الوسيط الخاص بك ، لكنك تحتاج إلى الطلب الأولي لتوصيله بنجاح.

تفو! حسنًا ، شكرًا _a طن_ على مساعدتك وصبرك. استغرق بعض الوقت للوصول إلى الجزء السفلي من ذلك. ما زلت أعتقد أنه نوع من المشكله التي لا أستطيع إدخالها في جميع الرؤوس والأشياء ثم تعديل الجسم ، لكن أعتقد أنه لا توجد طريقة للتغلب على ذلك.

simov

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

سوف تفشل من أجل الحذف. كما request.del هو الأسلوب وليس req.delete

أعلم أن هذا قديم ، لكنني واجهت صعوبة في إيجاد حل مناسب لذلك ، واعتقدت أنه قد يكون مفيدًا للآخرين.

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

كما هو موضح في https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options ، فإن إضافة هذا تجعله لا يتم إنهاء الدفق الجديد تلقائيًا عند إنهاء req.

بينما تسبب هذا في عمل هذا المثال ، لاحظت بعض المشكلات في إنشاء وكيل لطرق أخرى باستخدام هذا. يتسبب الجسم بطريقة ما في إغلاق كل شيء بشكل صحيح ، ولكن بدون وجود جسم لا يزال معلقًا. في حالة استخدام محلل json body ، لا ينبغي أن يتسبب ذلك في حدوث مشكلة لأنه يتسبب في قراءة الجسم على أنه {} حتى في حالة عدم وجود أحد ، ولكن بدون أحد ، لا يزال من الممكن أن يواجه مشكلات. الحل الحالي الخاص بي هو التحقق من وجود عنوان طول المحتوى في الطلب ، واستخدام كائن الخيارات إذا كان موجودًا.

JeffreyAngell فقط جرب الحل الخاص بك ، إنه يعمل. شكرا

هذا يناسبني:
"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();

}) ؛

دالة createOptions (method، targetUrl، json) {
targetUrl = targetUrl.replace ('jira /'، '') ؛
console.log ("serve:" + 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

نعم ، إذا ألقيت نظرة على تعليقي الأول مرة أخرى:

// ضعها قبل أي برمجيات وسيطة أخرى

يعالج body-parser الطلب قبل إدخال برمجيتك الوسيطة ، لكنك تحتاج إلى الطلب الأولي لتوصيله بنجاح.

لقد واجهت نفس المشكلة. هل يمكن أن تشرح لماذا يجب أن يكون ذلك قبل أن تتسبب برمجية وسيطة معينة في حالتي فقط في bodyParser الوسيطة التي تسبب الخطأ؟ يبدو أنه يمكنني استخدام برامج وسيطة أخرى ، مثل أدوار المستخدم ، والتعقيم دون أي مشاكل.

تعديل:
أريد فقط تحميل مساراتي بعد تحميل جميع البرامج الوسيطة ، لذلك اخترت هذا الحل الذي يستبعد bodyParser من جميع المسارات مع proxy في البداية:

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

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات