Menu
CoddyTech

إعادة التوجيه

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

تتغير الصفحات، وينبغي أن تظل الروابط القديمة في الإشارات المرجعية ونتائج البحث تقود إلى وجهة ما.

app.get('/old-prices', (req, res) => {
    res.redirect(301, '/prices');
});

يردّ إعادة التوجيه برمز حالة وترويسة Location تحدد عنوان URL الجديد، ويطلب المتصفح ذلك العنوان فورًا. وتضبط res.redirect كليهما.

يوضح رمز الحالة مدة استمرار النقل. يعني 301 أن النقل دائم: تتذكر المتصفحات ومحركات البحث العنوان الجديد وتتوقف عن طلب العنوان القديم. أما 302، وهو الافتراضي عند عدم تمرير أي رمز حالة، فيعني أن النقل مؤقت، لذا تواصل استخدام عنوان URL القديم.

challenge icon

التحدي

سهل

انتقلت صفحات القائمة القديمة نهائيًا إلى صفحات الوصفات، وينبغي أن تظل الإشارات المرجعية القديمة تعمل.

  1. في server.js، أضف مسارًا لـ /menu يعيد التوجيه إلى /recipes بالحالة 301، ما يعني أنه انتقل نهائيًا.
  2. أضف مسارًا لـ /menu/:id يعيد التوجيه إلى صفحة الوصفة ذات المعرّف نفسه، بالحالة 301 أيضًا: ينتقل /menu/3 إلى /recipes/3.

جرّب ذلك: اضغط «العب» وأرسل /menu/3. ينبغي أن ترى الحالة 301 وترويسة Location بقيمة /recipes/3. أرسل /menu: الحالة 301 مع Location بقيمة /recipes. أرسل /menu/42: الحالة 301 مع Location بقيمة /recipes/42. لا يتحقق إعادة التوجيه مما إذا كانت الوصفة 42 موجودة؛ إذ يستجيب /recipes/42 نفسه بعد ذلك بالحالة 404.

جرّب بنفسك

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.listen(3000, () => {
    console.log('Recipe Box is running on port 3000');
});

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

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