التحقق من المدخلات
جزء من قسم واجهات REST API في رحلة 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 أو " ". تحقّق من نوع كل حقل وحجمه قبل حفظه.
- في
routes/tasks.js، ضمن مسار POST، تحقّق منtitle:- يجب أن يكون نصًا.
- يجب ألا يكون فارغًا أو مكوّنًا من مسافات فقط.
- يجب ألا يتجاوز طوله 100 حرف، بما في ذلك المسافات.
إذا لم يتحقق أي من هذه الشروط، فأعِد استجابة بالحالة 400 وجملة
errorتتضمن الكلمةtitle، مثلًا:{ "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 API
1الموارد والأفعال
الموارد وعناوين URLالإنشاء باستخدام POST4المتصفحات والإصدارات والإعدادات
ما هو المصدرالسماح لمصدر آخرCORSصفحة تستدعي API2التحقق والأخطاء
التحقق من المدخلاتتدرّب بنفسك: Playground لـ Express