javascript - شرح - express vpn




خطأ: لا يمكن تعيين الرؤوس بعد إرسالها إلى العميل (11)

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

كان برنامجي يحتوي على رمز للتحقق من صحة الطلب والاستعلام عن DB. بعد التحقق من صحة وجود خطأ ، كنت أتصل بفهرس index.js مع أخطاء التحقق من الصحة. وإذا مرت عملية التحقق من الصحة ، فستواجهها وتنتقل إلى النهاية مع النجاح / الفشل.

    var error = validateRequestDetails("create",queryReq);
    if (error)
        callback(error, null);
   else
    some code 
    callback(null, success);

ما كان يحدث هو: فشل التحقق من الصحة في استدعاء الاتصال والحصول على استجابة. لكن لم يعود. لذلك لا يزال الأسلوب يذهب إلى ديسيبل وضرب النجاح / الفشل. وهي تستدعي معاودة الاتصال نفسها مرة أخرى مما يؤدي إلى تعيين الاستجابة مرتين الآن.

لذا الحل بسيط ، تحتاج إلى "إرجاع" رد الاتصال بحيث لا تستمر الطريقة في التنفيذ ، بمجرد حدوث الخطأ ، ومن ثم تعيين كائن الاستجابة مرة واحدة

  var error = validateRequestDetails("create",queryReq);
    if (error)
        callback(error, null);
        return;
    else
       some code 
       callback(null, success);

أنا جديدة إلى حد ما إلى Node.js وأواجه بعض المشكلات.

أستخدم Node.js 4.10 و Express 2.4.3.

عندما أحاول الوصول إلى http://127.0.0.1:8888/auth/facebook ، سيتم إعادة http://127.0.0.1:8888/auth/facebook_callback إلى http://127.0.0.1:8888/auth/facebook_callback .

تلقيت بعد ذلك الخطأ التالي:

Error: Can't render headers after they are sent to the client.
    at ServerResponse.<anonymous> (http.js:573:11)
    at ServerResponse._renderHeaders (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:64:25)
    at ServerResponse.writeHead (http.js:813:20)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:28:15
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:113:13
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/strategyExecutor.js:45:39)
    at [object Object].pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:32:3)
    at [object Object].halt (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:29:8)
    at [object Object].redirect (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:16:8)
    at [object Object].<anonymous> (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:77:15)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:195:11)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13)
    at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10)
    at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13)
    at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10)
    at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3)
    at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3)
    at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9

node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9
    at Array.<anonymous> (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session/memory.js:57:7)
    at EventEmitter._tickCallback (node.js:126:26)

فيما يلي الكود:

var fbId= "XXX";
var fbSecret= "XXXXXX";
var fbCallbackAddress= "http://127.0.0.1:8888/auth/facebook_callback"

var cookieSecret = "node";     // enter a random hash for security

var express= require('express');
var auth = require('connect-auth')
var app = express.createServer();


app.configure(function(){
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.session({secret: cookieSecret}));
    app.use(auth([
        auth.Facebook({
            appId : fbId,
            appSecret: fbSecret,
            callback: fbCallbackAddress,
            scope: 'offline_access,email,user_about_me,user_activities,manage_pages,publish_stream',
            failedUri: '/noauth'
        })
    ]));
    app.use(app.router);
});


app.get('/auth/facebook', function(req, res) {
  req.authenticate("facebook", function(error, authenticated) {
    if (authenticated) {
      res.redirect("/great");
      console.log("ok cool.");
      console.log(res['req']['session']);
    }
  });
});

app.get('/noauth', function(req, res) {
  console.log('Authentication Failed');
  res.send('Authentication Failed');
});

app.get('/great', function( req, res) {
  res.send('Supercoolstuff');
});

app.listen(8888);

هل لي أن أعرف ما هو الخطأ في الكود؟


إذا لم تتلق مساعدة من الأعلى: for noobs السبب وراء هذا الخطأ هو إرسال الطلب عدة مرات دعنا نفهم من بعض الحالات: - 1. "

module.exports = (req,res,next)=>{
        try{
           const decoded  = jwt.verify(req.body.token,"secret");
           req.userData = decoded;
           next();
        }catch(error){
            return res.status(401).json({message:'Auth failed'});
        }
        next();   
        }

`في الدعوة المذكورة أعلاه () مرتين سترفع خطأ

  1. router.delete('/:orderId', (req, res, next) => { Order.remove({_id:req.params.orderId},(err,data)=>{ if(err){ **res.status(500).json(err);** }else{ res.status(200).json(data); } *res.status(200).json(data);* }) })

هنا الرد هو إرسال مرتين تحقق ما إذا كنت قد أرسلت بالفعل استجابة


الكائن res في Express هو فئة فرعية من http.ServerResponse Node.js ' http.ServerResponse ( قراءة http.js المصدر ). يُسمح لك بالاتصال بـ res.setHeader(name, value) كلما أردت ذلك إلى أن تتصل بـ res.writeHead(statusCode) . بعد writeHead ، يتم خبز الرؤوس في ويمكنك فقط استدعاء res.write(data) ، وأخيراً res.end(data) .

الخطأ "خطأ: لا يمكن تعيين الرؤوس بعد إرسالها." يعني أنك موجود بالفعل في النص الأساسي أو الحالة النهائية ، لكن بعض الوظائف حاولت تعيين رأس أو statusCode. عندما ترى هذا الخطأ ، حاول البحث عن أي شيء يحاول إرسال رأس بعد أن يكون قد تم بالفعل كتابة جزء من النص. على سبيل المثال ، ابحث عن الاستدعاءات التي تسمى بطريق الخطأ مرتين ، أو أي خطأ يحدث بعد إرسال الجسم.

في حالتك ، اتصلت بـ res.redirect() ، مما جعل الاستجابة تصبح منتهية. ثم رمى res.req خطأ ( res.req هو null ). وبما أن الخطأ حدث داخل function(req, res, next) الفعلية function(req, res, next) (ليس ضمن رد اتصال) ، فقد تمكن Connect من التقاطه ثم حاول إرسال صفحة خطأ 500. ولكن منذ أن تم إرسال الرؤوس بالفعل ، ألقى setHeader 's setHeader الخطأ الذي رأيته.

قائمة شاملة لطرق استجابة Node.js / Express وعندما يجب استدعائها:

يجب أن يكون الرد في الرأس ويبقى في رأس الصفحة :

  1. res.writeContinue()
  2. res.statusCode = 404
  3. res.setHeader(name, value)
  4. res.getHeader(name)
  5. res.removeHeader(name)
  6. res.header(key[, val]) (صريح فقط)
  7. res.charset = 'utf-8' (Express فقط ؛ يؤثر فقط على الطرق الخاصة res.charset = 'utf-8' )
  8. res.contentType(type) (Express only)

يجب أن يكون الرد في الرأس ويصبح نصًا :

  1. res.writeHead(statusCode, [reasonPhrase], [headers])

يمكن أن تكون الاستجابة في الرأس / الجسم وتبقى في الجسم :

  1. res.write(chunk, encoding='utf8')

يمكن أن تكون الاستجابة إما في الرأس / الجسم وتنتهي :

  1. res.end([data], [encoding])

يمكن أن تكون الاستجابة إما في الرأس / الجسم وتبقى في حالتها الحالية:

  1. res.addTrailers(headers)

يجب أن يكون الرد في الرأس ويصبح منتهيًا :

  1. return next([err]) (اتصال / Express فقط)
  2. أي استثناءات داخل function(req, res, next) الوسيطة function(req, res, next) (Connect / Express فقط)
  3. res.send(body|status[, headers|status[, status]]) (Express only)
  4. res.attachment(filename) (التعبير فقط)
  5. res.sendfile(path[, options[, callback]]) (صريح فقط)
  6. res.json(obj[, headers|status[, status]]) (Express only)
  7. res.redirect(url[, status]) (عبر الإنترنت فقط)
  8. res.cookie(name, val[, options]) (صريح فقط)
  9. res.clearCookie(name[, options]) (صريح فقط)
  10. res.render(view[, options[, fn]]) (صريح فقط)
  11. res.partial(view[, options]) (صريح فقط)

بالنسبة إلى أي شخص يأتي إلى هذا ولم يساعد أي من الحلول الأخرى ، في حالتي هذه تتجلى على مسار تعامل مع تحميل الصور ولكن لم تتعامل مع المهلات ، وبالتالي إذا استغرق التحميل وقتًا طويلاً وتوقيته ، عندما تم تشغيل رد الاتصال بعد أن تم إرسال استجابة المهلة ، أدى استدعاء res.send () إلى التعطل حيث تم تعيين رؤوس الرسائل بالفعل لحساب المهلة.

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


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

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

نصيحة واحدة بسيطة لتبسيط التعليمات البرمجية. تخلص من app.configure () واتصل بـ app.use مباشرة في نطاق المستوى الأعلى الخاص بك.

انظر أيضا وحدة everyauth ، التي تفعل الفيسبوك وعشرات من مزودي مصادقة الطرف الثالث.


فقط أميل هذا. يمكنك تمرير الردود من خلال هذه الوظيفة:

app.use(function(req,res,next){
  var _send = res.send;
  var sent = false;
  res.send = function(data){
    if(sent) return;
    _send.bind(res)(data);
    sent = true;
};
  next();
});

في بعض الأحيان قد تحصل على هذا الخطأ عندما تحاول استدعاء الدالة next () بعد res.end أو res.send ، حاول أن تحذف إذا كان لديك next () بعد res.send أو res.end في وظيفتك. ملاحظة: هنا التالي () يعني بعد الرد على العميل مع ردك ( أي res.send أو res.end ) ، فأنت ما زلت تحاول تنفيذ بعض التعليمات البرمجية للرد مرة أخرى حتى لا يكون قانونيًا.

مثال:

router.get('/',function (req,res,next){
     res.send("request received");
     next(); // this will give you the above exception 
});

إزالة next() من وظيفة أعلاه ، وأنها ستعمل.


في حالتي ، حدث هذا مع React و postal.js عندما لم أقم بإلغاء الاشتراك من قناة في componentWillUnmount callback of my React component.


لقد واجهت هذه المشكلة نفسها وأدركت ذلك لأنني كنت أتصل بـ res.redirect بدون إفادة العودة ، لذلك كان يتم استدعاء الوظيفة التالية مباشرة بعد ذلك:

auth.annonymousOnly = function(req, res, next) {
    if (req.user) res.redirect('/');
    next();
};

الذي كان يجب أن يكون:

auth.annonymousOnly = function(req, res, next) {
    if (req.user) return res.redirect('/');
    next();
};

هذا النوع من الخطأ ستحصل عليه عند تمرير عبارات بعد إرسال استجابة.

فمثلا:

res.send("something response");
console.log("jhgfjhgsdhgfsdf");
console.log("sdgsdfhdgfdhgsdf");
res.send("sopmething response");

سيؤدي إلى الخطأ الذي تشاهده ، لأنه بمجرد إرسال الاستجابة ، لن يتم تنفيذ res.send التالي.

إذا كنت تريد القيام بأي شيء ، يجب عليك القيام بذلك قبل إرسال الاستجابة.


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

يستخدم HTTP دورة تتطلب استجابة واحدة لكل طلب. عندما يرسل العميل طلبًا (مثل POST أو GET) ، يجب على الخادم إرسال رد واحد فقط إليه.

رسالة الخطأ هذه:

خطأ: لا يمكن تعيين الرؤوس بعد إرسالها.

يحدث عادة عندما ترسل عدة ردود لطلب واحد. تأكد من استدعاء الوظائف التالية مرة واحدة لكل طلب:

  • res.json()
  • res.send()
  • res.redirect()
  • res.render()

(وعدد قليل آخر نادرًا ما يتم استخدامه ، تحقق من الإجابة المقبولة)

لن يعود رد الاتصال للطريق عندما يتم استدعاء وظائف الدقة هذه. سيستمر تشغيله حتى تصل إلى نهاية الدالة أو عبارة return. إذا كنت ترغب في العودة عند إرسال استجابة يمكنك القيام بذلك كما يلي: return res.send() .

خذ على سبيل المثال هذا الرمز:

app.post('/api/route1', function(req, res) {
  console.log('this ran');
  res.status(200).json({ message: 'ok' });
  console.log('this ran too');
  res.status(200).json({ message: 'ok' });
}

عند إرسال طلب POST إلى / api / route1 ، سيتم تشغيل كل سطر في معاودة الاتصال. لا يمكن تعيين res.json() بعد إرسال رسالة الخطأ لأنه يتم استدعاء res.json() مرتين ، مما يعني أنه يتم إرسال ردين.

يمكن إرسال استجابة واحدة فقط لكل طلب!

كان الخطأ في نموذج التعليمات البرمجية أعلاه واضح. المشكلة الأكثر شيوعًا هي عندما يكون لديك عدة فروع:

app.get('/api/company/:companyId', function(req, res) {
  const { companyId } = req.params;
  Company.findById(companyId).exec((err, company) => {
      if (err) {
        res.status(500).json(err);
      } else if (!company) {
        res.status(404).json();      // This runs.
      }
      res.status(200).json(company); // This runs as well.
    });
}

هذا الطريق مع رد الاتصال المرفقة يجد شركة في قاعدة بيانات. عند إجراء استعلام لشركة غير موجودة ، فسوف ندخل داخل الفرع else if فرعًا وإرسال استجابة 404. بعد ذلك ، سنستمر في البيان التالي الذي يرسل أيضًا ردًا. الآن أرسلنا إجابتين وستحدث رسالة الخطأ. يمكننا إصلاح هذا الرمز عن طريق التأكد من أننا نرسل ردًا واحدًا فقط:

.exec((err, company) => {
  if (err) {
    res.status(500).json(err);
  } else if (!company) {
    res.status(404).json();         // Only this runs.
  } else {
    res.status(200).json(company);
  }
});

أو بالعودة عند إرسال الرد:

.exec((err, company) => {
  if (err) {
    return res.status(500).json(err);
  } else if (!company) {
    return res.status(404).json();  // Only this runs.
  }
  return res.status(200).json(company);
});

الخاطئ الكبير هو وظائف غير متزامنة. خذ الوظيفة من this السؤال ، على سبيل المثال:

article.save(function(err, doc1) {
  if (err) {
    res.send(err);
  } else {
    User.findOneAndUpdate({ _id: req.user._id }, { $push: { article: doc._id } })
    .exec(function(err, doc2) {
      if (err) res.send(err);
      else     res.json(doc2);  // Will be called second.
    })

    res.json(doc1);             // Will be called first.
  }
});

لدينا هنا دالة غير متزامنة ( findOneAndUpdate() ) في نموذج التعليمات البرمجية. في حالة عدم وجود أخطاء ( err findOneAndUpdate() سيتم استدعاء findOneAndUpdate() . لأن هذه الوظيفة غير متزامنة سيتم res.json(doc1) على الفور. افترض عدم وجود أخطاء في findOneAndUpdate() . سيتم بعد ذلك استدعاء res.json(doc2) في else . لقد تم الآن إرسال جوابين ولا تظهر رسالة خطأ رؤوس الرسائل التي لا يمكن تعيينها .

الإصلاح ، في هذه الحالة ، سيكون لإزالة res.json(doc1) . لإرسال كلتا المستندات مرة أخرى إلى العميل ، يمكن كتابة res.json() في آخر كـ res.json({ article: doc1, user: doc2 }) .