Menu
CoddyTech

验证输入

本课属于 Coddy Node.js 学习旅程的“REST API”单元。第 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,并提供一条包含 title 一词的 error 错误信息,例如:

    { "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,带有 { "done": true } 的 PATCH 仍返回 200。保持 PUT 不变;Schema 也会用 zod 检查它。

试试看:向 /api/tasks 发送 POST 请求,内容为 { "title": 123 }:你会得到 400 和标题错误。发送 POST 请求,内容为 { "title": " " }:同样会得到 400。使用 { "done": "yes" } 对 PATCH /api/tasks/2 发送请求:会得到 400 和 done 错误,而 GET /api/tasks/2 仍会显示 "done": false。使用 { "done": true } 对 PATCH /api/tasks/2 发送请求:会得到 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中的所有课

自己练习: Express 在线编辑器