Menu
CoddyTech

أول برمجية وسيطة لك

جزء من قسم أساسيات Express في رحلة Node.js على Coddy. الدرس 13 من 23.

هناك أعمال تُنفَّذ مع كل طلب، مثل كتابة سطر في السجل، ونسخها إلى كل مسار لن ينتهي أبدًا. لكل طلب طريقة، في req.method: GET لجلب شيء ما، وPOST لإرسال شيء ما.

let visits = 0;

app.use((req, res, next) => {
    visits++;
    next();
});

الدالة التي تُمرَّر إلى app.use هي برمجية وسيطة: يُشغّلها Express مع كل طلب، بالترتيب الذي أُضيفت به، وقبل المسارات التي تليها. وتتلقى وسيطًا ثالثًا هو next. يؤدي استدعاء next() إلى تمرير الطلب إلى ما سيأتي بعد ذلك. أما البرمجية الوسيطة التي لا تستجيب ولا تستدعي next() فتترك الطلب منتظرًا إلى الأبد.

إن express.static من الفصل السابق برمجية وسيطة أيضًا: فهي تستجيب عندما تعثر على ملف، وتستدعي next() عندما لا تعثر عليه.

challenge icon

التحدي

سهل

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

  1. في server.js، أضف وسيطًا باستخدام app.use مباشرةً بعد const app = express();. عند وضعه هناك، يعمل قبل كل مسار.
  2. لكل طلب، يطبع سطرًا واحدًا في وحدة التحكم: طريقة الطلب، ثم مسافة، ثم مساره. يطبع الطلب إلى /recipes/2 GET /recipes/2. توجد الطريقة في req.method (GET وPOST وما إلى ذلك)، والمسار في req.url.
  3. بعد الطباعة، يستدعي next()، لكي ينتقل الطلب إلى المسارات.

جرّب ذلك: اضغط على «تشغيل» وأرسل /recipes/2. ينبغي أن ترى Pancakes، وأن تعرض وحدة التحكم GET /recipes/2. أرسل /api/recipes: ستظهر القائمة كالمعتاد، وستعرض وحدة التحكم GET /api/recipes. أرسل /nope: ستحصل على 404، وستظل وحدة التحكم تعرض GET /nope، لأن الوسيط يعمل قبل أن يبحث Express عن مسار.

جرّب بنفسك

import { escapeHtml } from './html.js';
import express from 'express';
import { recipes } from './data.js';

const app = express();

app.use(express.static('public'));

app.get('/', (req, res) => {
    const items = recipes.map((r) => `<li><a href="/recipes/${r.id}">${escapeHtml(r.title)}</a></li>`);
    res.send(`<link rel="stylesheet" href="/style.css"><h1>Recipe Box</h1><ul>${items.join('')}</ul>`);
});

// ترسل type('text') نصًا عاديًا، لذا يظهر كل عنوان في سطر مستقل.
app.get('/recipes', (req, res) => {
    const { tag } = req.query;
    const list = tag ? recipes.filter((recipe) => recipe.tags.includes(tag)) : recipes;
    res.type('text').send(list.map((recipe) => recipe.title).join('\n'));
});

app.get('/recipes/:id', (req, res) => {
    const recipe = recipes.find((r) => r.id === Number(req.params.id));
    if (!recipe) {
        res.status(404).send('No such recipe');
        return;
    }
    res.send(recipe.title);
});

app.get('/api/recipes', (req, res) => {
    res.json(recipes);
});

app.get('/api/recipes/:id', (req, res) => {
    const recipe = recipes.find((r) => r.id === Number(req.params.id));
    if (!recipe) {
        res.status(404).json({ error: 'No such recipe' });
        return;
    }
    res.json(recipe);
});

app.get('/about', (req, res) => {
    res.send('Recipe Box collects family recipes.');
});

app.get('/menu', (req, res) => {
    res.redirect(301, '/recipes');
});

app.get('/menu/:id', (req, res) => {
    res.redirect(301, `/recipes/${req.params.id}`);
});

app.listen(3000, () => {
    console.log('Recipe Box is running on port 3000');
});
quiz iconاختبر نفسك

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

جميع دروس أساسيات Express

تدرّب بنفسك: Playground لـ Express