Suchseite
Teil des Abschnitts Express-Grundlagen der Node.js-Journey von Coddy. Lektion 23 von 23.
Eine Suchseite ist ein Formular, und der Server beantwortet es wie jede andere GET-Anfrage.
<form action="/books" method="get">
<input name="author">
<button>Find</button>
</form>Beim Absenden dieses Formulars wird der Browser zu /books?author= weitergeleitet, gefolgt von dem, was in das Feld mit dem Namen author eingegeben wurde. Die Route liest den Wert aus req.query. Wenn du das Wort in das value-Attribut des Felds zurückschreibst, bleibt es nach der Suche sichtbar; es stammt vom Besucher, also musst du es maskieren.
Aufgabe
EinfachRecipe Box erhält eine Suchseite. Ein Besucher gibt ein Wort ein, und die Seite listet die Rezepte auf, deren Titel es enthält.
- Füge in
server.jsoberhalb des 404-Handlers eine asynchrone Route für/searchhinzu. Sie liest das Wort ausreq.query.qoder verwendet eine leere Zeichenfolge, wenn keines vorhanden ist. - Die Seite enthält immer ein Suchformular:
<form action="/search" method="get">mit einem Textfeld namensqund einer Schaltfläche. Beim Absenden wird/search?q=gefolgt vom Wort geöffnet. - Im Feld wird das gesuchte Wort angezeigt: Nach der Suche nach
currystehtcurryim Feld. Schreibe das Wort in dasvalue-Attribut des Feldes und lasse es durchescapeHtmllaufen, da es vom Besucher stammt. - Liste unterhalb des Formulars für jedes Rezept aus
loadRecipes(), dessen Titel das Wort enthält, einen Link auf; Groß- und Kleinschreibung wird dabei ignoriert: Sowohlsoupals auchSOUPfinden Tomato Soup. Jeder Link führt zur Seite des Rezepts und zeigt dessen maskierten Titel an, wie auf der Startseite:<a href="/recipes/1">Tomato Soup</a>. - Wenn nach einem Wort gesucht wurde und kein Titel es enthält, zeige statt der Liste den Text
No recipes foundan.
Probiere es aus: Drücke auf „Ausführen“ und rufe /search auf: Die Seite zeigt das leere Formular. Gib SOUP in das Feld ein und drücke die Schaltfläche. Die Seite listet Tomato Soup auf, und im Feld steht weiterhin SOUP. Suche nach pizza: No recipes found. Suche nach fish: Fish & Chips wird korrekt angezeigt. Sende dann {"title": "Pea Soup", "tags": ["soup"]} per POST an /api/recipes und suche erneut nach soup: Pea Soup steht ebenfalls in der Liste.
Probier es selbst
import { escapeHtml } from './html.js';
import { dayOfYear } from './day-of-year.js';
import express from 'express';
import { randomUUID } from 'node:crypto';
import { recipes } from './data.js';
import recipesRouter, { loadRecipes } from './routes/recipes.js';
import reviewsRouter from './routes/reviews.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 unformatierten Text, sodass jeder Titel in einer eigenen Zeile steht.
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.use('/api/recipes', recipesRouter);
app.use('/api/recipes/:id/reviews', reviewsRouter);
app.get('/api/today', async (req, res) => {
const day = dayOfYear(req.query.date);
if (day === null) {
res.status(400).json({ error: 'Use a date like 2026-03-01' });
return;
}
const recipes = await loadRecipes();
res.json(recipes[day % recipes.length]);
});
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.use((req, res) => {
if (req.path.startsWith('/api/')) {
res.status(404).json({ error: 'Not found' });
return;
}
res.status(404).send('<h1>Page not found</h1><p><a href="/">Back to Recipe Box</a></p>');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Something went wrong' });
});
app.listen(3000, () => {
console.log('Recipe Box is running on port 3000');
});
Alle Lektionen in Express-Grundlagen
Übe selbstständig: Express-Playground