Handler 404
Termasuk bagian Dasar-Dasar Express dalam perjalanan Node.js di Coddy. Pelajaran 14 dari 20.
Express menampilkan halamannya sendiri untuk path yang tidak dikenal, Cannot GET /x, yang tidak sesuai dengan situs maupun API-mu.
app.use((req, res) => {
res.status(404).send('No forecast here. Try /weather/oslo');
});Middleware tanpa path dijalankan untuk setiap permintaan yang mencapainya. Letakkan middleware ini setelah semua route dan setelah express.static: permintaan yang sudah ditangani tidak akan sampai ke sana, jadi middleware ini hanya melihat permintaan yang belum ditangani. Middleware ini menangani permintaan, jadi tidak memerlukan next.
req.path adalah path tanpa query string, jadi req.path.startsWith('/api/') membedakan panggilan API dari halaman.
Tantangan
Mudah- Path di bawah
/api/yang tidak ditangani apa pun akan mendapat404dengan{ "error": "Not found" }. - Path lainnya akan menampilkan halaman HTML
404yang bertuliskanPage not found.
Kirim GET /api/nope, buka /nope di tampilan halaman, lalu kirim GET /api/recipes/1.
Coba sendiri
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('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"');
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') mengirim teks biasa, sehingga setiap judul muncul di baris tersendiri.
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');
});
Semua pelajaran di Dasar-Dasar Express
Latihan sendiri: Playground Express