Menu
CoddyTech

L’ordre compte

Fait partie de la section Les bases d’Express du Journey Node.js de Coddy. Leçon 14 sur 23.

Express exécute les intergiciels et les routes dans l’ordre où votre code les ajoute, et une route qui répond met fin à la requête à cet endroit.

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

app.use((req, res, next) => {
    res.set('Cache-Control', 'no-store');
    next();
});

res.set(name, value) ajoute un en-tête à la réponse et doit être exécuté avant l’envoi de celle-ci. Ici, GET /bikes répond avant que l’intergiciel ne soit atteint ; sa réponse ne reçoit donc jamais l’en-tête : l’intergiciel ne voit que les requêtes auxquelles aucune route située au-dessus de lui n’a répondu. Les intergiciels qui doivent agir sur chaque réponse se placent au-dessus des routes et de express.static.

challenge icon

Défi

Facile

Chaque réponse reçoit un identifiant, afin que vous puissiez retrouver la requête d’un visiteur dans votre journal lorsqu’il signale un problème. Toutes les réponses en ont besoin : les pages, les données de l’API et les fichiers de public.

  1. Dans server.js, importez randomUUID depuis node:crypto. Chaque appel renvoie un nouvel identifiant aléatoire, comme 3b241101-e2bb-4255-8caf-4136c566a962.
  2. Ajoutez un middleware qui définit un en-tête de réponse nommé X-Request-Id, contenant un nouveau randomUUID() pour chaque requête, puis appelle next().
  3. Placez-le de manière à ce que chaque requête le traverse, y compris les fichiers envoyés par express.static. Une réponse ne reçoit l’en-tête que si le middleware s’exécute avant qu’un élément ne réponde.

Essayez : appuyez sur « Jouer » et envoyez /api/recipes deux fois. Dans l’onglet Headers, X-Request-Id a une valeur différente à chaque fois. Envoyez /style.css et / : les deux comportent également un X-Request-Id.

Essayez vous-même

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

const app = express();

app.use((req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
});

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') envoie du texte brut, de sorte que chaque titre s’affiche sur sa propre ligne.
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');
});

Toutes les leçons de Les bases d’Express

Entraînez-vous par vous-même : Playground Express