Node.js و Express

التوجيه في Express

22 دقيقة الدرس 8 من 40

فهم التوجيه في Express

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

هيكل المسار الأساسي

تعريف المسار له الهيكل التالي:

app.METHOD(PATH, HANDLER)

حيث:

  • app هو نسخة من Express
  • METHOD هو طريقة طلب HTTP (get، post، put، delete، إلخ) بأحرف صغيرة
  • PATH هو المسار/نقطة النهاية على الخادم
  • HANDLER هو الدالة التي يتم تنفيذها عند مطابقة المسار

طرق مسارات HTTP

يدعم Express جميع طرق HTTP القياسية. فيما يلي الطرق الأكثر استخدامًا:

GET - استرجاع البيانات

تستخدم طلبات GET لاسترداد البيانات من الخادم:

// الحصول على جميع المستخدمين app.get('/users', (req, res) => { res.json([ { id: 1, name: 'أحمد' }, { id: 2, name: 'فاطمة' } ]); }); // الحصول على مستخدم محدد app.get('/users/:id', (req, res) => { const userId = req.params.id; res.json({ id: userId, name: 'أحمد' }); });

POST - إنشاء البيانات

تستخدم طلبات POST لإنشاء موارد جديدة:

app.post('/users', (req, res) => { const { name, email } = req.body; // في التطبيق الحقيقي، احفظ في قاعدة البيانات res.status(201).json({ id: 3, name, email, message: 'تم إنشاء المستخدم بنجاح' }); });

PUT - تحديث البيانات (استبدال كامل)

تستبدل طلبات PUT المورد بالكامل:

app.put('/users/:id', (req, res) => { const userId = req.params.id; const { name, email, age } = req.body; // في التطبيق الحقيقي، حدّث في قاعدة البيانات res.json({ id: userId, name, email, age, message: 'تم تحديث المستخدم بالكامل' }); });

PATCH - تحديث البيانات (تحديث جزئي)

تحدّث طلبات PATCH الحقول المحددة فقط:

app.patch('/users/:id', (req, res) => { const userId = req.params.id; const updates = req.body; // الحقول المراد تحديثها فقط res.json({ id: userId, ...updates, message: 'تم تحديث المستخدم جزئيًا' }); });

DELETE - حذف البيانات

تزيل طلبات DELETE الموارد:

app.delete('/users/:id', (req, res) => { const userId = req.params.id; // في التطبيق الحقيقي، احذف من قاعدة البيانات res.json({ message: `تم حذف المستخدم ${userId} بنجاح` }); });

جميع الطرق

التعامل مع جميع طرق HTTP لمسار:

app.all('/secret', (req, res) => { console.log(`الوصول إلى القسم السري عبر ${req.method}`); res.send('منطقة سرية'); });
ملاحظة: الترتيب الذي تحدد به المسارات مهم. يطابق Express المسارات بالترتيب الذي تم تعريفها به. ضع المسارات الأكثر تحديدًا قبل العامة.

معاملات المسار

معاملات المسار هي أجزاء URL المسماة المستخدمة لالتقاط القيم في مواضع محددة في عنوان URL. يتم تخزين القيم الملتقطة في كائن req.params.

معامل واحد

app.get('/users/:id', (req, res) => { const userId = req.params.id; res.send(`معرف المستخدم: ${userId}`); }); // مثال: /users/123 ← معرف المستخدم: 123

معاملات متعددة

app.get('/users/:userId/posts/:postId', (req, res) => { const { userId, postId } = req.params; res.json({ user: userId, post: postId, message: `الحصول على المنشور ${postId} من المستخدم ${userId}` }); }); // مثال: /users/5/posts/42 // النتيجة: { user: '5', post: '42', message: ... }

أنماط المعاملات

يمكنك استخدام التعبيرات النمطية للتحقق من صحة المعاملات:

// مطابقة المعرفات الرقمية فقط app.get('/products/:id(\\d+)', (req, res) => { res.send(`معرف المنتج: ${req.params.id}`); }); // مطابقة المعرفات بـ 5 أرقام بالضبط app.get('/orders/:orderId(\\d{5})', (req, res) => { res.send(`معرف الطلب: ${req.params.orderId}`); });

المعاملات الاختيارية

// علامة ? تجعل المعامل اختياريًا app.get('/users/:id?/profile', (req, res) => { if (req.params.id) { res.send(`الملف الشخصي للمستخدم ${req.params.id}`); } else { res.send('الملف الشخصي للمستخدم الحالي'); } }); // يطابق كلاً من: // /users/123/profile // /users/profile
نصيحة: قم دائمًا بالتحقق من صحة معاملات المسار وتنظيفها قبل استخدامها، خاصة عند الاستعلام عن قواعد البيانات لمنع هجمات الحقن.

معاملات سلسلة الاستعلام

سلاسل الاستعلام هي أزواج مفتاح-قيمة تظهر بعد ? في عنوان URL. يتم الوصول إليها عبر req.query.

معامل استعلام واحد

app.get('/search', (req, res) => { const searchTerm = req.query.q; res.json({ query: searchTerm, results: [`نتيجة 1 لـ ${searchTerm}`, `نتيجة 2 لـ ${searchTerm}`] }); }); // مثال: /search?q=express // النتيجة: { query: 'express', results: [...] }

معاملات استعلام متعددة

app.get('/products', (req, res) => { const { category, minPrice, maxPrice, sort } = req.query; res.json({ category: category || 'الكل', priceRange: { min: minPrice || 0, max: maxPrice || Infinity }, sortBy: sort || 'الصلة' }); }); // مثال: /products?category=electronics&minPrice=100&maxPrice=500&sort=price

القيم الافتراضية وتحويل النوع

app.get('/items', (req, res) => { // معاملات الاستعلام دائمًا سلاسل نصية const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 10; const sortOrder = req.query.sort || 'asc'; res.json({ page, limit, sortOrder, message: `الصفحة ${page} مع ${limit} عنصرًا لكل صفحة` }); });
ملاحظة: يتم استلام معاملات الاستعلام دائمًا كسلاسل نصية. تحتاج إلى تحويلها إلى أرقام أو قيم منطقية إذا لزم الأمر.

وحدة الموجه (Router)

بالنسبة للتطبيقات الأكبر، من الأفضل تنظيم المسارات في وحدات منفصلة باستخدام express.Router(). هذا ينشئ معالجات مسارات معيارية وقابلة للتركيب.

إنشاء موجه

أنشئ ملفًا routes/users.js:

const express = require('express'); const router = express.Router(); // جميع المسارات هنا نسبية لنقطة التثبيت // GET /users router.get('/', (req, res) => { res.json({ message: 'الحصول على جميع المستخدمين' }); }); // GET /users/:id router.get('/:id', (req, res) => { res.json({ message: `الحصول على المستخدم ${req.params.id}` }); }); // POST /users router.post('/', (req, res) => { res.status(201).json({ message: 'تم إنشاء المستخدم' }); }); // PUT /users/:id router.put('/:id', (req, res) => { res.json({ message: `تحديث المستخدم ${req.params.id}` }); }); // DELETE /users/:id router.delete('/:id', (req, res) => { res.json({ message: `حذف المستخدم ${req.params.id}` }); }); module.exports = router;

استخدام الموجه في التطبيق الرئيسي

في app.js الرئيسي الخاص بك:

const express = require('express'); const app = express(); const usersRouter = require('./routes/users'); app.use(express.json()); // تثبيت الموجه عند /users app.use('/users', usersRouter); // الآن جميع المسارات في usersRouter مسبوقة بـ /users // GET /users ← router.get('/') // GET /users/123 ← router.get('/:id') // POST /users ← router.post('/') app.listen(3000);

موجهات متعددة

يمكنك إنشاء موجهات متعددة لموارد مختلفة:

// routes/users.js const usersRouter = require('./routes/users'); // routes/posts.js const postsRouter = require('./routes/posts'); // routes/comments.js const commentsRouter = require('./routes/comments'); // app.js app.use('/api/users', usersRouter); app.use('/api/posts', postsRouter); app.use('/api/comments', commentsRouter);

الموجهات المتداخلة

يمكنك تداخل الموجهات لإنشاء هياكل مسارات هرمية:

// routes/users.js const express = require('express'); const router = express.Router(); const postsRouter = require('./posts'); router.get('/', (req, res) => { res.json({ message: 'جميع المستخدمين' }); }); // تداخل موجه المنشورات تحت المستخدمين router.use('/:userId/posts', postsRouter); module.exports = router;
// routes/posts.js const express = require('express'); const router = express.Router({ mergeParams: true }); // مهم! router.get('/', (req, res) => { // الوصول إلى معامل المسار الأصلي const userId = req.params.userId; res.json({ message: `منشورات للمستخدم ${userId}` }); }); router.get('/:postId', (req, res) => { const { userId, postId } = req.params; res.json({ message: `المنشور ${postId} من المستخدم ${userId}` }); }); module.exports = router;
// app.js app.use('/users', usersRouter); // ينتج عنه مسارات: // GET /users ← جميع المستخدمين // GET /users/5/posts ← منشورات للمستخدم 5 // GET /users/5/posts/42 ← المنشور 42 من المستخدم 5
تحذير: عند استخدام الموجهات المتداخلة، قم بتعيين mergeParams: true في الموجه الفرعي للوصول إلى معاملات المسار الأصلية. بدون هذا، سيكون req.params.userId غير محدد في موجه المنشورات.

تجميع المسارات والتسلسل

يمكنك تسلسل معالجات المسارات لنفس المسار:

تسلسل الطرق

router.route('/users/:id') .get((req, res) => { res.json({ message: 'الحصول على المستخدم' }); }) .put((req, res) => { res.json({ message: 'تحديث المستخدم' }); }) .delete((req, res) => { res.json({ message: 'حذف المستخدم' }); });

معالجات متعددة لمسار واحد

يمكنك تمرير دوال استدعاء متعددة كمعالجات:

// دوال برمجية وسيطة const validateUser = (req, res, next) => { if (!req.body.name) { return res.status(400).json({ error: 'الاسم مطلوب' }); } next(); }; const checkAuth = (req, res, next) => { // التحقق من المصادقة const isAuthenticated = true; // مبسط if (!isAuthenticated) { return res.status(401).json({ error: 'غير مصرح' }); } next(); }; // تطبيق معالجات متعددة router.post('/users', checkAuth, validateUser, (req, res) => { res.status(201).json({ message: 'تم إنشاء المستخدم' }); });

مصفوفة المعالجات

const handlers = [ checkAuth, validateUser, (req, res) => { res.json({ message: 'تم اجتياز جميع الفحوصات' }); } ]; router.post('/users', handlers);

أنماط المسارات

يدعم Express أنماطًا مختلفة لمسارات المسارات:

أنماط السلاسل النصية

// يطابق أي مسار يبدأ بـ /ab app.get('/ab*cd', (req, res) => { res.send('مطابق: abcd, abxcd, ab123cd, إلخ.'); }); // علامة + تعني واحد أو أكثر app.get('/ab+cd', (req, res) => { res.send('يطابق: abcd, abbcd, abbbcd, إلخ.'); }); // علامة ? تعني اختياري app.get('/ab?cd', (req, res) => { res.send('يطابق: acd, abcd'); });

أنماط التعبيرات النمطية

// مطابقة أي مسار يحتوي على 'fly' app.get(/.*fly$/, (req, res) => { res.send('يطابق: butterfly, dragonfly, إلخ.'); }); // مطابقة 3 أرقام بالضبط app.get(/^\/\d{3}$/, (req, res) => { res.send('يطابق: /123, /456, إلخ.'); });

أفضل ممارسات تنظيم المسارات

هيكل المجلد الموصى به

project/ ├── app.js ├── routes/ │ ├── index.js // ملف الموجه الرئيسي │ ├── users.js │ ├── posts.js │ └── auth.js ├── controllers/ │ ├── usersController.js │ ├── postsController.js │ └── authController.js └── middleware/ ├── auth.js └── validation.js

فصل المتحكمات

احتفظ بمنطق المسار في ملفات متحكم منفصلة:

// controllers/usersController.js exports.getAllUsers = (req, res) => { res.json({ message: 'الحصول على جميع المستخدمين' }); }; exports.getUserById = (req, res) => { res.json({ message: `الحصول على المستخدم ${req.params.id}` }); }; exports.createUser = (req, res) => { res.status(201).json({ message: 'تم إنشاء المستخدم' }); }; exports.updateUser = (req, res) => { res.json({ message: 'تم تحديث المستخدم' }); }; exports.deleteUser = (req, res) => { res.json({ message: 'تم حذف المستخدم' }); };
// routes/users.js const express = require('express'); const router = express.Router(); const usersController = require('../controllers/usersController'); router.get('/', usersController.getAllUsers); router.get('/:id', usersController.getUserById); router.post('/', usersController.createUser); router.put('/:id', usersController.updateUser); router.delete('/:id', usersController.deleteUser); module.exports = router;
نصيحة: استخدم أسماء مسارات وصفية واتبع اصطلاحات RESTful لمسارات API. على سبيل المثال: GET /users, POST /users, GET /users/:id, PUT /users/:id, DELETE /users/:id

اصطلاحات التوجيه RESTful

تتبع واجهات برمجة التطبيقات RESTful الاصطلاحات القياسية لتسمية المسارات:

// المورد: المستخدمون GET /users // الحصول على جميع المستخدمين GET /users/:id // الحصول على مستخدم محدد POST /users // إنشاء مستخدم جديد PUT /users/:id // تحديث المستخدم بالكامل PATCH /users/:id // تحديث جزء من المستخدم DELETE /users/:id // حذف المستخدم // الموارد المتداخلة GET /users/:userId/posts // الحصول على المنشورات حسب المستخدم GET /users/:userId/posts/:postId // الحصول على منشور محدد حسب المستخدم POST /users/:userId/posts // إنشاء منشور للمستخدم
تمرين: أنشئ API RESTful كاملًا لمدونة:
  1. أنشئ هيكل مشروع مع ملفات مسارات ومتحكم منفصلة
  2. قم بتنفيذ مسارات لثلاثة موارد: المستخدمين والمنشورات والتعليقات
  3. مسارات المستخدمين:
    • GET /api/users - الحصول على جميع المستخدمين
    • GET /api/users/:id - الحصول على المستخدم بواسطة ID
    • POST /api/users - إنشاء مستخدم
    • PUT /api/users/:id - تحديث المستخدم
    • DELETE /api/users/:id - حذف المستخدم
  4. مسارات المنشورات (هيكل مماثل للمستخدمين)
  5. مسار متداخل: GET /api/users/:userId/posts - الحصول على جميع منشورات المستخدم
  6. قم بتنفيذ معاملات الاستعلام للترقيم: /api/posts?page=1&limit=10
  7. أنشئ برمجية وسيطة للتحقق من الحقول المطلوبة
  8. اختبر جميع المسارات باستخدام Postman أو Thunder Client
  9. أضف معالج 404 للمسارات غير المحددة