Konflikte
Teil des Abschnitts REST-APIs der Node.js-Journey von Coddy. Lektion 9 von 18.
Eine zweite Aufgabe namens „Rechnung senden“ macht die Liste unübersichtlich, aber ihr Inhalt ist gültig. Das ist ein 409 Conflict: Die Anfrage steht im Widerspruch zu dem, was bereits gespeichert ist. Ein 400 bedeutet, dass der Inhalt selbst fehlerhaft ist.
const taken = users.some((u) => u.name.toLowerCase() === name.toLowerCase());
if (taken) {
return sendError(res, 409, 'conflict', 'That username is taken');
}Vergleiche in Kleinbuchstaben, da Menschen „Ada“ und „ada“ als denselben Namen lesen. Wenn eine Anfrage etwas umbenennt, überspringe das Element selbst bei der Suche. Andernfalls erhält ein Benutzer, der das Formular speichert, ohne seinen Namen zu ändern, einen Konflikt mit sich selbst.
Aufgabe
Einfach- Ein Titel, den eine andere Aufgabe bereits hat, unabhängig von der Groß- und Kleinschreibung, führt zu einer Antwort mit Statuscode 409 und dem Code
conflict: bei POST sowie bei PATCH und PUT, wenn sie einen Titel festlegen. - Eine Aufgabe darf ihren eigenen Titel behalten.
Sende POST mit { "title": "book the meeting room" }, dann PUT /api/tasks/2 mit { "title": "Book the meeting room", "done": true }.
Probier es selbst
import { Router } from 'express';
import { z } from 'zod';
import { sendError, sendInvalid } from '../errors.js';
import { nextId, tasks } from '../store.js';
const router = Router();
// Eine vollständige Aufgabe, wie PUT sie sendet. .strict() lehnt Felder ab, die nicht aufgeführt sind.
const Task = z
.object({
title: z.string().trim().min(1).max(100),
done: z.boolean(),
})
.strict();
const NewTask = Task.partial({ done: true });
const TaskChanges = Task.partial();
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 sendError(res, 404, 'not_found', 'Task not found');
}
res.json(task);
});
router.post('/', (req, res) => {
const result = NewTask.safeParse(req.body);
if (!result.success) {
return sendInvalid(res, result.error);
}
const { title, done = false } = result.data;
const task = { id: nextId(), title, done, 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 sendError(res, 404, 'not_found', 'Task not found');
}
const result = TaskChanges.safeParse(req.body);
if (!result.success) {
return sendInvalid(res, result.error);
}
Object.assign(task, result.data);
res.json(task);
});
router.put('/:id', (req, res) => {
const index = tasks.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return sendError(res, 404, 'not_found', 'Task not found');
}
const result = Task.safeParse(req.body);
if (!result.success) {
return sendInvalid(res, result.error);
}
const { id, createdAt } = tasks[index];
tasks[index] = { id, ...result.data, 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 sendError(res, 404, 'not_found', 'Task not found');
}
tasks.splice(index, 1);
res.status(204).end();
});
export default router;