سنتعلم اليوم كيفية إنشاء خادم HTTP، معالجة الطلبات الواردة، وإرسال استجابات متنوعة للعميل.
1. تهيئة بيئة الخادم الأساسية (Basic Server Setup)
الخطوة الأولى في بناء أي تطبيق ويب باستخدام Node.js هي إنشاء خادم HTTP بسيط. سنستخدم الوحدة النمطية http المدمجة في Node.js لتحقيق ذلك. هذه الوحدة توفر القدرة على التعامل مع بروتوكولات HTTP.
const http = require('http'); // استيراد وحدة HTTP الأساسية في Node.js لإنشاء خادم ويب
const PORT = 3000; // تحديد المنفذ الذي سيستمع عليه الخادم
// إنشاء الخادم باستخدام دالة createServer
// هذه الدالة تتلقى دالة رد نداء (callback) تُنفذ عند كل طلب وارد
const server = http.createServer((req, res) => {
// هنا ستتم معالجة الطلبات والاستجابات لاحقاً
});
// جعل الخادم يستمع على المنفذ المحدد
server.listen(PORT, () => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ: http://localhost:${PORT}</code>); // رسالة تأكيد عند بدء تشغيل الخادم
});وحدة 'http' هي حجر الزاوية لبناء خوادم الويب في Node.js، وتوفر كل الأدوات اللازمة للتعامل مع بروتوكول HTTP. الدالة createServer() تتلقى دالة رد نداء تُنفذ لكل طلب HTTP جديد.2. معالجة الطلبات الواردة (Handling Incoming Requests)
عندما يتصل العميل بالخادم ويرسل طلباً، يتم استدعاء دالة رد النداء التي مررناها إلى createServer. هذه الدالة تتلقى كائنين أساسيين: req (Request) و res (Response). الكائن req يحتوي على جميع المعلومات المتعلقة بالطلب الوارد.
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم استلام طلب جديد:</code>); // تسجيل رسالة عند وصول طلب
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">المسار (URL): ${req.url}</code>); // عرض مسار الطلب (مثل /, /about, /api/data)
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الطريقة (Method): ${req.method}</code>); // عرض طريقة الطلب (GET, POST, PUT, DELETE)
// في هذه المرحلة، لم نرسل أي استجابة للعميل بعد، لذلك سيبقى الطلب معلقاً
});
server.listen(PORT, () => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ: http://localhost:${PORT}</code>);
});الكائن 'req' (Request) يمثل الطلب الوارد من العميل ويحتوي على جميع المعلومات المتعلقة بالطلب مثل الرؤوس (headers)، المسار (URL)، الطريقة (Method)، وجسم الطلب (body) إذا كان موجوداً.
3. إرسال الاستجابات (Sending Responses)
بعد معالجة الطلب، يحين وقت إرسال استجابة إلى العميل. الكائن res (Response) هو المسؤول عن هذه المهمة. يمكننا تعيين رؤوس HTTP، رمز الحالة، وكتابة محتوى الاستجابة قبل إرسالها إلى العميل.
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم استلام طلب جديد: ${req.method} ${req.url}</code>);
// تعيين رأس الاستجابة (Content-Type) للإشارة إلى أننا نرسل نصاً عادياً
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
// تعيين رمز حالة HTTP للاستجابة (200 OK يعني نجاح الطلب)
res.statusCode = 200;
// كتابة محتوى الاستجابة. يمكن استدعاء res.write عدة مرات.
res.write('مرحباً بك في خادم Node.js الخاص بنا!\n');
res.write(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">لقد استقبلت طلباً من نوع: ${req.method}\n</code>);
res.write(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">على المسار: ${req.url}\n</code>);
// إنهاء الاستجابة وإرسالها إلى العميل. يجب استدعاء res.end مرة واحدة لكل طلب.
res.end('هذه نهاية الاستجابة.');
});
server.listen(PORT, () => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ: http://localhost:${PORT}</code>);
});الكائن 'res' (Response) هو المسؤول عن بناء وإرسال الاستجابة إلى العميل. يمكنك تعيين الرؤوس باستخدامres.setHeader()، رمز الحالة باستخدامres.statusCode، وكتابة البيانات في جسم الاستجابة باستخدامres.write()، ثم إنهاء الاستجابة وإرسالها باستخدامres.end().
4. التعامل مع مسارات مختلفة (Handling Different Routes)
في التطبيقات الحقيقية، لا نريد أن يستجيب الخادم بنفس الطريقة لجميع الطلبات. نحتاج إلى توجيه الطلبات المختلفة إلى معالجات مختلفة بناءً على المسار (URL) وطريقة HTTP (GET, POST, إلخ). يمكننا تحقيق ذلك باستخدام بنى شرطية بسيطة.
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم استلام طلب جديد: ${req.method} ${req.url}</code>);
res.setHeader('Content-Type', 'text/html; charset=utf-8'); // تعيين نوع المحتوى الافتراضي إلى HTML
if (req.url === '/') {
// إذا كان المسار هو الجذر (الصفحة الرئيسية)
res.statusCode = 200;
res.end('<h1>الصفحة الرئيسية</h1><p>أهلاً وسهلاً بك في الصفحة الرئيسية!</p>');
} else if (req.url === '/about') {
// إذا كان المسار هو /about
res.statusCode = 200;
res.end('<h1>عنّا</h1><p>نحن فريق من المطورين المتحمسين لـ Node.js.</p>');
} else if (req.url === '/api/data' && req.method === 'GET') {
// مثال على نقطة نهاية API تستجيب ببيانات JSON
res.setHeader('Content-Type', 'application/json; charset=utf-8'); // تغيير نوع المحتوى إلى JSON
res.statusCode = 200;
const data = {
message: 'هذه بيانات من API',
timestamp: new Date().toISOString()
};
res.end(JSON.stringify(data)); // إرسال بيانات JSON بعد تحويلها إلى نص
} else {
// لأي مسار آخر غير موجود
res.statusCode = 404; // رمز حالة "غير موجود"
res.end('<h1>404 - لم يتم العثور على الصفحة</h1><p>عذراً، الصفحة التي تبحث عنها غير موجودة.</p>');
}
});
server.listen(PORT, () => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ: http://localhost:${PORT}</code>);
});فهم 'req.url' و 'req.method' يسمح لنا ببناء منطق توجيه (routing) أساسي لتلبية الطلبات المختلفة بناءً على المسار ونوع العملية المطلوبة. هذه هي أساس بناء واجهات برمجة التطبيقات (APIs) وتطبيقات الويب الديناميكية.
الكود النهائي الكامل
هذا هو الكود المجمع الذي يوضح دورة حياة الطلب والاستجابة بالكامل مع معالجة المسارات المختلفة.
const http = require('http'); // استيراد وحدة HTTP الأساسية لإنشاء خادم ويب
const PORT = 3000; // تحديد المنفذ الذي سيستمع عليه الخادم
// إنشاء الخادم باستخدام دالة createServer
const server = http.createServer((req, res) => {
// تسجيل تفاصيل الطلب الوارد للمراقبة في طرفية الخادم
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">تم استلام طلب جديد: ${req.method} ${req.url}</code>);
// تعيين الرأس الافتراضي لنوع المحتوى (يمكن تغييره لاحقاً حسب المسار)
res.setHeader('Content-Type', 'text/html; charset=utf-8');
// منطق التوجيه (Routing) بناءً على مسار الطلب (req.url) وطريقته (req.method)
if (req.url === '/') {
// معالجة الطلب للصفحة الرئيسية
res.statusCode = 200; // تعيين رمز الحالة 200 (OK) للإشارة إلى نجاح الطلب
res.end('<h1>الصفحة الرئيسية</h1><p>أهلاً وسهلاً بك في الصفحة الرئيسية!</p>');
} else if (req.url === '/about') {
// معالجة الطلب لصفحة "عنّا"
res.statusCode = 200;
res.end('<h1>عنّا</h1><p>نحن فريق من المطورين المتحمسين لـ Node.js.</p>');
} else if (req.url === '/api/data' && req.method === 'GET') {
// معالجة طلب API للحصول على بيانات
res.setHeader('Content-Type', 'application/json; charset=utf-8'); // تغيير نوع المحتوى إلى JSON
res.statusCode = 200;
const data = {
message: 'هذه بيانات من API',
timestamp: new Date().toISOString()
};
res.end(JSON.stringify(data)); // إرسال البيانات كـ JSON بعد تحويلها من كائن JavaScript إلى نص
} else {
// معالجة الطلبات للمسارات غير الموجودة (404 Not Found)
res.statusCode = 404; // تعيين رمز الحالة 404 للإشارة إلى عدم العثور على المورد
res.end('<h1>404 - لم يتم العثور على الصفحة</h1><p>عذراً، الصفحة التي تبحث عنها غير موجودة.</p>');
}
});
// جعل الخادم يستمع على المنفذ المحدد
server.listen(PORT, () => {
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ: http://localhost:${PORT}</code>); // رسالة تأكيد عند بدء تشغيل الخادم
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">يمكنك زيارة الروابط التالية لاختبار الخادم:</code>);
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- الصفحة الرئيسية: http://localhost:${PORT}/</code>);
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- صفحة عنّا: http://localhost:${PORT}/about</code>);
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- نقطة نهاية API: http://localhost:${PORT}/api/data</code>);
console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">- صفحة غير موجودة: http://localhost:${PORT}/nonexistent</code>);
});النتيجة المتوقعة
عند تشغيل السكربت باستخدام الأمر node server.js في طرفية سطر الأوامر، سيقوم الخادم بالاستماع على المنفذ 3000. يمكنك فتح متصفح الويب وزيارة العناوين التالية:
http://localhost:3000/: سيعرض لك صفحة HTML بعنوان "الصفحة الرئيسية" ويطبع في الطرفية تفاصيل الطلب.http://localhost:3000/about: سيعرض لك صفحة HTML بعنوان "عنّا" ويطبع في الطرفية تفاصيل الطلب.http://localhost:3000/api/data: سيعرض لك كائن JSON يحتوي على رسالة ووقت حالي، ويطبع في الطرفية تفاصيل الطلب.http://localhost:3000/nonexistent(أو أي مسار آخر غير معرف): سيعرض لك صفحة HTML بعنوان "404 - لم يتم العثور على الصفحة" ويطبع في الطرفية تفاصيل الطلب.
في كل مرة يتم فيها الوصول إلى مسار، ستظهر رسالة في طرفية Node.js توضح تفاصيل الطلب (المسار والطريقة)، مما يوضح دورة حياة الطلب والاستجابة بشكل فعال.