טיפול בשגיאות
חלק מהיחידה יסודות Express במסלול ה-Node.js של Coddy. שיעור 15 מתוך 20.
כשנתיב זורק שגיאה, Express משיב באמצעות דף השגיאה שלו, שאינו JSON ומציג פרטים שנועדו לך.
app.get('/api/fines/:member', (req, res) => {
throw new Error('Fines database is down');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Try again later' });
});תוכנת ביניים עם ארבעה ארגומנטים, (err, req, res, next), היא מטפל שגיאות. Express סופר את הארגומנטים: עם שלושה ארגומנטים זו תוכנת ביניים רגילה, ושגיאות מדלגות עליה. כשנתיב זורק שגיאה, Express מדלג על תוכנת הביניים הרגילה וקורא למטפל השגיאות עם השגיאה. הוסף אותו בסוף, אחרי מטפל 404. רשום את הפרטים ביומן לעצמך; שלח ללקוח הודעה קצרה.
ערך שגוי של ?by= הוא טעות של המבקר, ולכן עדיין מתקבלת שגיאת 400. שגיאה מיועדת למקרים שהמבקר לא יכול לתקן, כמו מתכון שנשמר בלי מספר המנות.
אתגר
קלGET /api/recipes/:id/scale?by=2מחזיר{ "title", "servings" }, כשהמנות מוכפלות.- גורם שאינו מספר חיובי:
400עם{ "error": "Scale by a positive number" }. - מתכון ללא
servingsזורקRecipe <id> has no servings. - מטפל שגיאות רושם את השגיאה ביומן ומחזיר
500עם{ "error": "Something went wrong" }.
שלחו ?by=2 ואת ?by=lots. לאחר מכן, שלחו { "title": "Toast" } אל /api/recipes ושנו את קנה המידה של מתכון 5.
נסו בעצמכם
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') שולח טקסט פשוט, כך שכל כותרת מופיעה בשורה משלה.
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.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.listen(3000, () => {
console.log('Recipe Box is running on port 3000');
});
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.