אימות קלט
חלק מהיחידה REST APIs במסלול ה-Node.js של Coddy. שיעור 6 מתוך 25.
לקוח יכול לשלוח כל דבר בגוף הבקשה, ו-if (!name) עוצר רק ערכים ריקים. 123, true ו-["x"] הם ערכי אמת ועוברים. גם מחרוזת של רווחים עוברת: תפסו אותה באמצעות trim(), כי ' '.trim() הוא ''. בדקו את הטיפוס של כל שדה באמצעות typeof, ולאחר מכן בדקו את המגבלות שלו:
const { seats } = req.body ?? {};
if (typeof seats !== 'number' || !Number.isInteger(seats) || seats < 1) {
return res.status(400).json({ error: 'seats must be a whole number from 1' });
}נסחו את ההודעה עבור מי שקורא את התגובה: ציינו את שם השדה ואת הערך שהוא צריך לקבל, כדי שיידעו מה לתקן. בקשת PATCH בודקת רק את השדות שנשלחו בה, מכיוון שהשדות האחרים אינם משתנים.
אתגר
קלכרגע if (!title) מאפשר לעבור כותרת כמו 123 או " ". בדקו את הסוג ואת האורך של כל שדה לפני ששומרים אותו.
- בנתיב POST שבקובץ
routes/tasks.js, בדקו אתtitle:- הוא חייב להיות מחרוזת.
- הוא לא יכול להיות ריק או להכיל רווחים בלבד.
- אורכו יכול להיות עד 100 תווים, כולל רווחים.
אם אחד מהתנאים האלה אינו מתקיים, החזירו תגובת סטטוס 400 עם משפט שגיאה שמכיל את המילה
error, למשל:{ "error": "title must be a string of 1 to 100 characters" } - בנתיב PATCH, בדקו רק את השדות שהגוף שולח:
- אם הוא שולח
title, חלים אותו כלל ואותו סוג של שגיאה. - אם הוא שולח
done, הערך חייב להיותtrueאוfalse. אחרת, החזירו תגובת סטטוס 400 עם שגיאה שמכילה את המילהdone:
PATCH שנדחה לא משנה דבר.{ "error": "done must be true or false" } - אם הוא שולח
- גופים תקינים ממשיכים לעבוד: POST עדיין מחזיר 201, ו-PATCH עם
{ "done": true }עדיין מחזיר 200. השאירו את PUT כפי שהוא; גם Schemas בודק אותו באמצעות zod.
נסו זאת: שלחו POST עם { "title": 123 } אל /api/tasks: תקבלו 400 עם שגיאת הכותרת. שלחו POST עם { "title": " " }: גם כאן תקבלו 400. שלחו PATCH /api/tasks/2 עם { "done": "yes" }: תקבלו 400 עם שגיאת done, ו-GET /api/tasks/2 עדיין יציג "done": false. שלחו PATCH /api/tasks/2 עם { "done": true }: תקבלו 200.
נסו בעצמכם
import { Router } from 'express';
import { nextId, tasks } from '../store.js';
const router = Router();
router.get('/', (req, res) => {
res.json(tasks);
});
router.get('/:id', (req, res) => {
const task = tasks.find((t) => t.id === Number(req.params.id));
if (!task) {
return res.status(404).json({ error: 'Task not found' });
}
res.json(task);
});
router.post('/', (req, res) => {
const { title } = req.body ?? {};
if (!title) {
return res.status(400).json({ error: 'title is required' });
}
const task = { id: nextId(), title, done: false, createdAt: new Date().toISOString() };
tasks.push(task);
res.status(201).location(`/api/tasks/${task.id}`).json(task);
});
router.patch('/:id', (req, res) => {
const task = tasks.find((t) => t.id === Number(req.params.id));
if (!task) {
return res.status(404).json({ error: 'Task not found' });
}
const { title, done } = req.body ?? {};
if (title !== undefined) task.title = title;
if (done !== undefined) task.done = done;
res.json(task);
});
router.put('/:id', (req, res) => {
const index = tasks.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'Task not found' });
}
const { title, done } = req.body ?? {};
if (title === undefined || done === undefined) {
return res.status(400).json({ error: 'PUT needs every field: title and done' });
}
const { id, createdAt } = tasks[index];
tasks[index] = { id, title, done, createdAt };
res.json(tasks[index]);
});
router.delete('/:id', (req, res) => {
const index = tasks.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'Task not found' });
}
tasks.splice(index, 1);
res.status(204).end();
});
export default router;
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה REST APIs
תרגלו בעצמכם: Express אונליין