Menu
CoddyTech

Obsługa błędu 404

Część sekcji Podstawy Express ścieżki Node.js w Coddy. Lekcja 14 z 20.

Express odpowiada na nieznaną ścieżkę własną stroną, Cannot GET /x, która nie pasuje ani do Twojej witryny, ani do API.

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

Middleware bez ścieżki uruchamia się dla każdego żądania, które do niego dotrze. Umieść go za wszystkimi trasami i za express.static: żądania, na które odpowiedziały te elementy, nigdy do niego nie dotrą, więc zobaczy tylko te, na które nic nie odpowiedziało. Odpowiada na żądanie, więc nie potrzebuje next.

req.path to ścieżka bez ciągu zapytania, więc req.path.startsWith('/api/') pozwala odróżnić wywołanie API od strony.

challenge icon

Wyzwanie

Łatwy
  • Ścieżka w /api/, na którą nic nie odpowiada, zwraca 404 z { "error": "Not found" }.
  • Każda inna ścieżka zwraca stronę HTML 404 z komunikatem Page not found.

Wyślij GET /api/nope, otwórz /nope w widoku strony, a następnie wyślij GET /api/recipes/1.

Spróbuj swoich sił

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'));

const escapeHtml = (text) =>
    text
        .replaceAll('&', '&')
        .replaceAll('<', '&lt;')
        .replaceAll('>', '&gt;')
        .replaceAll('"', '&quot;');

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') wysyła zwykły tekst, więc każdy tytuł wyświetla się w osobnym wierszu.
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');
});

Wszystkie lekcje w sekcji Podstawy Express