Trình xử lý 404
Thuộc phần Kiến thức cơ bản về Express trong hành trình Node.js của Coddy. Bài học 16 trên 23.
Express trả lời đường dẫn không xác định bằng trang riêng của nó, Cannot GET /x, không phù hợp với trang web hay API của bạn.
app.use((req, res) => {
res.status(404).send('No forecast here. Try /weather/oslo');
});Một middleware (phần mềm trung gian) không có đường dẫn sẽ chạy với mọi yêu cầu đến được nó. Đặt middleware này sau tất cả các route và sau express.static: những yêu cầu mà chúng đã xử lý sẽ không đến đây, nên middleware này chỉ nhận những yêu cầu chưa được xử lý. Middleware này tự trả lời, nên không cần next.
req.path là đường dẫn không có chuỗi truy vấn, nên req.path.startsWith('/api/') giúp phân biệt lời gọi API với yêu cầu trang.
Thử thách
DễNhững đường dẫn không tồn tại sẽ hiển thị trang Cannot GET mặc định của Express. Hãy thay bằng nội dung của riêng bạn: JSON cho API và một trang thân thiện cho khách truy cập.
- Trong
server.js, sau tất cả các route và sauexpress.static, hãy thêm mộtapp.usecuối cùng không có đường dẫn. Các yêu cầu đã được xử lý sẽ không đến đây, nên nó chỉ nhận những yêu cầu chưa được xử lý. - Khi đường dẫn bắt đầu bằng
/api/, máy chủ sẽ phản hồi với trạng thái 404 và JSON sau:{"error": "Not found"} - Với mọi đường dẫn khác, máy chủ sẽ phản hồi với trạng thái 404 và một trang HTML có dòng chữ
Page not found, chẳng hạn như<h1>Page not found</h1>. Thêm liên kết quay lại trang chủ cũng là một điểm cộng.
Thử nhé: Nhấn Chạy và gửi yêu cầu đến /api/nope. Bạn sẽ thấy trạng thái 404 và {"error":"Not found"}. Gửi yêu cầu đến /nope: trạng thái 404, hiển thị thành một trang có dòng chữ Page not found. Gửi yêu cầu đến /api/recipes/1: trạng thái 200 và Tomato Soup, cho thấy các route của bạn vẫn được xử lý trước.
Tự làm thử
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') gửi văn bản thuần túy, nên mỗi tiêu đề hiển thị trên một dòng riêng.
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');
});
Tất cả bài học trong Kiến thức cơ bản về Express
1Máy chủ đầu tiên của bạn
Xin chào, ExpressModule httpThêm tuyến đườngTham số tuyến đường3Middleware
Middleware đầu tiên của bạnTự luyện tập: Playground Express