Menu
CoddyTech

Der 404-Handler

Teil des Abschnitts Express-Grundlagen der Node.js-Journey von Coddy. Lektion 16 von 23.

Express beantwortet einen unbekannten Pfad mit einer eigenen Seite, Cannot GET /x, die weder zu deiner Website noch zu deiner API passt.

app.use((req, res) => {
    res.status(404).send('No forecast here. Try /weather/oslo');
});

Eine Middleware ohne Pfad wird bei jeder Anfrage ausgeführt, die sie erreicht. Füge sie nach jeder Route und nach express.static ein: Anfragen, die dort beantwortet wurden, gelangen nie bis zu ihr, deshalb sieht sie nur diejenigen, die nirgendwo beantwortet wurden. Sie beantwortet die Anfrage selbst und benötigt daher kein next.

req.path ist der Pfad ohne die Abfragezeichenfolge. Daher unterscheidet req.path.startsWith('/api/') einen API-Aufruf von einer Seite.

challenge icon

Aufgabe

Einfach

Pfade, die nicht existieren, zeigen die einfache Cannot GET-Seite von Express. Ersetze sie durch deine eigene: JSON für die API und eine freundliche Seite für Besucher.

  1. Füge in server.js nach jeder Route und nach express.static als Letztes ein app.use ohne Pfad hinzu. Anfragen, auf die bereits etwas geantwortet hat, erreichen diese Middleware nie, daher sieht sie nur die Anfragen, die von keiner anderen Middleware behandelt wurden.
  2. Wenn der Pfad mit /api/ beginnt, antworte mit Status 404 und diesem JSON: {"error": "Not found"}
  3. Bei jedem anderen Pfad antworte mit Status 404 und einer HTML-Seite, die die Worte Page not found enthält, zum Beispiel <h1>Page not found</h1>. Ein Link zurück zur Startseite ist eine nette Ergänzung.

Probiere es aus: Drücke „Spielen“ und sende /api/nope. Du solltest den Status 404 und {"error":"Not found"} sehen. Sende /nope: Status 404, angezeigt als Seite mit dem Text Page not found. Sende /api/recipes/1: Status 200 und Tomato Soup – daran erkennst du, dass deine Routen weiterhin zuerst kommen.

Probier es selbst

import { escapeHtml } from './html.js';
import express from 'express';
import { randomUUID } from 'node:crypto';
import { recipes } from './data.js';

const app = express();

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

app.use((req, res, next) => {
    res.set('X-Request-Id', randomUUID());
    next();
});

app.use(express.json());

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') sendet reinen Text, sodass jeder Titel in einer eigenen Zeile erscheint.
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.post('/api/recipes', (req, res) => {
    const { title, tags } = req.body;
    if (!title) {
        res.status(400).json({ error: 'A recipe needs a title' });
        return;
    }
    const recipe = { id: recipes.length + 1, title, tags: tags ?? [] };
    recipes.push(recipe);
    res.status(201).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');
});

Alle Lektionen in Express-Grundlagen

Übe selbstständig: Express-Playground