Menu
CoddyTech

التحقق من المدخلات

جزء من قسم واجهات 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 من الحقول التي أُرسلت إليه فقط، لأن الحقول الأخرى لا تتغير.

challenge icon

التحدي

سهل

حاليًا، يسمح if (!title) بمرور عنوان مثل 123 أو " ". تحقّق من نوع كل حقل وحجمه قبل حفظه.

  1. في routes/tasks.js، ضمن مسار POST، تحقّق من title:
    • يجب أن يكون نصًا.
    • يجب ألا يكون فارغًا أو مكوّنًا من مسافات فقط.
    • يجب ألا يتجاوز طوله 100 حرف، بما في ذلك المسافات.

    إذا لم يتحقق أي من هذه الشروط، فأعِد استجابة بالحالة 400 وجملة error تتضمن الكلمة title، مثلًا:

    { "error": "title must be a string of 1 to 100 characters" }
  2. في مسار PATCH، تحقّق من الحقول التي يرسلها الجسم فقط:
    • إذا أرسل title، فتُطبَّق القاعدة نفسها والنوع نفسه من الأخطاء.
    • إذا أرسل done، فيجب أن تكون قيمته true أو false. وإلا فأعِد استجابة بالحالة 400 وخطأ يتضمن الكلمة done:
    { "error": "done must be true or false" }
    طلب PATCH المرفوض لا يغيّر شيئًا.
  3. تظل الطلبات الصحيحة تعمل: يظل 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;
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس واجهات REST API

تدرّب بنفسك: Playground لـ Express