Menu
CoddyTech

Obsługa błędu 404

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

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

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

Funkcja pośrednicząca bez ścieżki uruchamia się dla każdego żądania, które do niej dotrze. Umieść ją za wszystkimi trasami i za express.static: żądania, na które udzieliły odpowiedzi, nigdy tam nie docierają, więc widzi tylko te, na które nikt nie odpowiedział. Udziela odpowiedzi, 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żki, które nie istnieją, wyświetlają standardową stronę Cannot GET Express. Zastąp ją własną: JSON-em dla API i przyjazną stroną dla odwiedzających.

  1. W pliku server.js, po każdej trasie i po express.static, dodaj na samym końcu jedno app.use bez ścieżki. Żądania, na które coś już odpowiedziało, nigdy do niego nie dotrą, więc obsłuży tylko te, których nic nie obsłużyło.
  2. Gdy ścieżka zaczyna się od /api/, odpowiedz statusem 404 i tym JSON-em: {"error": "Not found"}
  3. Dla każdej innej ścieżki odpowiedz statusem 404 i stroną HTML zawierającą tekst Page not found, na przykład <h1>Page not found</h1>. Miłym dodatkiem będzie link prowadzący z powrotem na stronę główną.

Wypróbuj: Naciśnij „Uruchom” i wyślij /api/nope. Powinien pojawić się status 404 i {"error":"Not found"}. Wyślij /nope: status 404, wyświetlony jako strona z tekstem Page not found. Wyślij /api/recipes/1: status 200 i Tomato Soup, co pokazuje, że Twoje trasy nadal są obsługiwane w pierwszej kolejności.

Spróbuj swoich sił

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') wysyła zwykły tekst, więc każdy tytuł pojawia 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

Poćwicz samodzielnie: Edytor online Express