验证输入
本课属于 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 只检查它收到的字段,因为其他字段不会改变。
挑战
简单现在,if (!title) 会让 123 或 " " 这样的标题通过。保存之前,请检查每个字段的类型和大小。
- 在
routes/tasks.js的 POST 路由中,检查title:- 它必须是字符串。
- 它不能为空,也不能只包含空格。
- 它的长度不能超过 100 个字符,空格也计入长度。
如果其中任何一项不符合要求,就返回状态码 400,并提供一条包含
title一词的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,带有
{ "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;
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
REST API中的所有课
自己练习: Express 在线编辑器