Express.js / الأساسي: مسؤولية الطلب والاستجابة / الدرس 4
الاستجابات والحالة والترويسات والتمثيل
ابن استجابة HTTP متماسكة وميز الإرسال واختيار الحالة واكتمال التوصيل إلى العميل.
في هذه الصفحة
أهداف الدرس
- اختر حالة الرد ونوع المحتوى والجسم العام بقرار صريح.
- اشرح headersSent وHEAD ومدققات المخبأ دون افتراض إمكان إعادة كتابة الرد بعد بدء التدفق.
تحمل استجابة HTTP وعوداً مترابطة
تشكل حالة HTTP والترويسات والجسم عقد استجابة واحداً. تختار res.status الحالة ولا تنهي الرد وحدها. تسلسل res.json تمثيلاً بصيغة JSON وترسله، بينما تعالج res.send أشكال جسم أخرى مدعومة. اختر شكلاً عاماً ثابتاً للخطأ بدلاً من عرض كائن الاستثناء الخام. قد تعبر 201 عن إنشاء وفق عقد المجال، ولا يحمل رد 204 محتوى في الجسم. ينبغي أن تعكس هذه الأعراف النتيجة الحقيقية للتطبيق، لا أن تختار لأن المعالج وصل إلى آخر سطر فيه. وجود رد منسق لا يثبت بحد ذاته حفظ أثر دائم.
حدد تمثيلاً مسموحاً واحداً قبل الإرسال
يضبط الجزء الحالة وسياسة المخبأ قبل إرسال بطاقة اصطناعية خاصة. تمثل private مثالاً مقصوداً لبيانات مرتبطة بالهوية، ولا تستبدل ضبط الوصول. حدد هل يسمح بتخزين الرد أصلاً، ومدة إعادة استعماله، وإمكان التخزين لدى وسيط مشترك. لا تدخل قيماً غير موثوقة في الترويسات ولا تبن إعادة توجيه من إدخال عشوائي. يوضح الجزء ترتيب الخطوات فقط ويستخدم بطاقة سبق تفويضها؛ فلا يصادق مستدعياً ولا يقيس سلوك مخبأ متصفح أو وكيل. يجب أن تسبق قرارات الجسم والترويسات الالتزام الفعلي بالإرسال.
app.get("/account/course-card", async (req,res) => {
const card = await catalog.cardForPrincipal(req.principal);
res.status(200);
res.set("Cache-Control","private, no-store");
return res.json({id:card.id,title:card.title});
});
يكون الرد JSON بسياسة المخبأ المعلنة. ما زال التطبيق مسؤولاً عن بناء الهوية وتفويض الوصول إلى catalog.
لا تعاد صياغة الترويسات بعد إرسالها
بعد الالتزام بإرسال الترويسات أو بايتات الجسم، لا يمكن عادة تحويل فشل لاحق إلى رد JSON جديد ونظيف بحالة أخرى. تشير res.headersSent إلى إرسال الترويسات، ولا تثبت أن العميل استقبل الجسم كاملاً وعالجه. ينبغي لمعالج الأخطاء تمرير الحالة على النحو المناسب عند تجاوز الحد، ويحتاج التدفق سياسة فشل وتنظيف خاصة به. تمنع العودة من الدالة الأسطر المحلية اللاحقة لكنها لا تلغي مصدراً يعمل تلقائياً. افصل بين اكتمال الإرسال والانقطاع والإلغاء، لأن الخلط بينها قد يسبب إرسالاً ثانياً أو موارد مستمرة بعد فقد العميل.
اختبر خطة الرد ذي الجسم والخالي منه
تمثل الدالة المحلية قرار التطبيق قبل وجود أي بايتات HTTP. تخطط نتيجة الحذف الناجح لرد 204 بلا جسم، وتخطط حالة الغياب المسبق لخطأ JSON بحالة 404 وفق هذا العقد. يمكن لمجال آخر اختيار سياسة مختلفة للحذف المكرر؛ المهم أن يصرح النموذج باختياره. يفيد فصل خطة النتيجة عن موائم النقل في اختبار القرار مستقلاً. لا تثبت التأكيدات إسقاط الجسم في Node أو تسلسل الترويسات أو سلوك رد Express الفعلي؛ فهذه حدود دمج تحتاج أدلة أخرى، حتى لو بدت الخطة المحلية صحيحة تماماً.
function deletePlan(removed) {
return removed ? {status:204} : {status:404,body:{code:"COURSE_NOT_FOUND"}};
}
const success = deletePlan(true);
const absent = deletePlan(false);
console.assert(success.status === 204 && !Object.hasOwn(success,"body"));
console.assert(absent.status === 404 && absent.body.code === "COURSE_NOT_FOUND");
ينجح التأكيدان وفق سياسة الحذف المكرر المعلنة. تعيد الدالة كائنات فقط ولا ترسل استجابة HTTP.
راجع HEAD والتخزين الشرطي مع التمثيل
تصف HEAD ترويسات GET المقابلة دون جسم محتوى. يستطيع Express استعمال معالج GET لها إذا لم يعالجها مسار HEAD سابق، ولذلك قد تبقى قراءات المجال مكلفة ما لم يصمم التطبيق خلاف ذلك. قد تخفض الطلبات الشرطية ومدققاتها النقل، لكنها تحتاج حداثة وهوية تراعيان التمثيل. لا تمنح ETag صلاحية وصول، ولا يجوز لمخبأ مشترك إعادة تمثيل خاص بهوية إلى أخرى. راجع الحالة وVary والسياسة والمدققات معاً؛ فارتفاع نسبة إصابات المخبأ لا يثبت صحة الجمهور الذي تصله البيانات أو وفاء الرد بعقد الحداثة.
الأخطاء الشائعة وإصلاحها
يختار المعالج الحالة ولا يرسل
- لماذا يحدث؟
- اختلط اختيار الحالة بإكمال الرد.
- كيف تصلحه؟
- أكمل المسار بطريقة send أو json أو end المناسبة.
- كيف تتجنب تكراره؟
- راجع كل فرع وحدد مسؤول إكماله.
يحاول خطأ متأخر إرسال JSON ثانية
- لماذا يحدث؟
- تجاهل التنفيذ حد إرسال الترويسات.
- كيف تصلحه؟
- افحص headersSent واستعمل مسار تمرير الخطأ الموثق.
- كيف تتجنب تكراره؟
- اختبر الفشل بعد أول بايتات تدفق مستقلاً.
يعاد استعمال بطاقة خاصة لمستخدم آخر
- لماذا يحدث؟
- لم يوافق جمهور المخبأ أو مفتاحه نطاق التمثيل.
- كيف تصلحه؟
- صحح سياسة الخصوصية وعمر المخبأ الواعي بالهوية.
- كيف تتجنب تكراره؟
- اختبر هويتين متتابعتين عبر طبقة المخبأ الفعلية.
اختبر فهمك
1. هل تثبت headersSent استهلاك العميل جسم JSON كاملاً بنجاح؟
اعرض الحل المشروح
لا؛ تحدد حد التزام الخادم بالترويسات. قد يفشل الاتصال قبل التوصيل الكامل أو يفشل العميل في المعالجة. استخدم ملاحظات الإكمال والانقطاع المناسبة، ولا تعتبر العلامة دليلاً على أن المستخدم أقر فعل العمل.
2. لماذا قد تشغل HEAD بحث قاعدة داخل معالج GET؟
اعرض الحل المشروح
قد يوجه Express الطلب إلى معالج GET المقابل عند غياب معالج HEAD سابق. منع الجسم لا يمنع تلقائياً الكود الذي يبنيه. راجع مسارات البيانات الوصفية المكلفة والتفويض صراحة، ولا تستنتج الكلفة من غياب محتوى منقول.
أهم ما تعلمته
- تشكل الحالة والترويسات والجسم عقد رد يلتزم به الإرسال.
- يغير المخبأ وHEAD النقل دون منح صلاحية أو إزالة عمل المصدر تلقائياً.