0%

Anfrage-Bodys und eine JSON-API

Ressourcen und Sammlungen

Dein Server kann jetzt einen Anfrage-Body lesen und prüfen, dass er - ist und als solches gekennzeichnet. Bevor du den Code schreibst, der ein Buch speichert, entscheide, wohin ein neues Buch kommt, welche Anfrage ein vorhandenes ändert und was jede Anfrage antwortet. Diese Entscheidung ist die Form der API, und jeder Client, der sie verwendet, hängt von ihr ab.

Eine URL benennt ein Ding

In Kapitel 2 hast du die Idee kennengelernt, dass eine eine Ressource benennt und die sagt, was damit geschehen soll. GET fragt sie ab und ist sicher: Es ändert nichts. PUT und DELETE sind : Zweimal gesendet hinterlassen sie den Server im selben Zustand wie einmal gesendet. POST ist keins von beidem.

Die Leseliste hat zwei Arten von Ressourcen:

  • Die Sammlung, /books: die aller Bücher.

  • Ein Element, /books/7: ein Buch, benannt durch seine ID.

Ein Schreibzugriff geht entweder an die Sammlung, um ihr ein Buch hinzuzufügen, oder an ein Element, um dieses Buch zu ändern oder zu entfernen. Das ergibt vier Operationen, oft CRUD genannt: create, read, update, delete (anlegen, lesen, ändern, löschen). So bildet diese API sie ab:

AnfrageWirkungErfolgBody der Antwort
POST /booksfügt ein Buch unter einer ID hinzu, die der Server wählt201 Created, mit location: /books/{id}das gespeicherte Buch
GET /bookskeine200 OK{"books": [...]}
GET /books/{id}keine200 OKdas Buch
PUT /books/{id}ersetzt dieses Buch durch den Body200 OKdas gespeicherte Buch
DELETE /books/{id}entfernt dieses Buch204 No Contentkeiner

Als Regal gezeichnet sieht dieselbe Tabelle so aus:

/books        the shelf         POST adds a book with a new id      -> 201
                                GET reads the whole shelf           -> 200
/books/7      one book on it    GET reads it                        -> 200
                                PUT replaces it                     -> 200
                                DELETE takes it off the shelf       -> 204

Jede andere Methode auf einem der beiden Pfade bekommt weiterhin 405, wobei allow jetzt auflistet, was die Tabelle erlaubt: GET, HEAD, POST auf der Sammlung und GET, HEAD, PUT, DELETE auf einem Element. Ein PUT /books würde die ganze Sammlung ersetzen, und ein DELETE /books würde sie leeren; diese API bietet keins von beidem an.

201 Created und 204 No Content sind beides 2xx-Erfolge, also behandelt ein Client, der zuerst nach der Klasse entscheidet, wie es deiner seit Kapitel 3 tut, sie bereits als Erfolg. Lektion 4 sieht sich an, was jeder davon auf der Leitung verlangt.

Wer die ID wählt

Bei POST wählt der Server. Der Client sendet einen Titel und einen Autor an /books; der Server gibt dem Buch die nächste freie ID und sagt dem Client im Header location, wo es jetzt liegt. Der Client kennt die ID nicht, bevor er fragt, und deshalb ergibt POST /books/7 hier keinen Sinn.

Bei PUT wählt die URL. PUT /books/7 sagt „das Buch unter /books/7 soll jetzt dies sein“, also kommt die ID aus dem Pfad und nie aus dem Body: Ein Body, der "id": 99 sagt, ersetzt trotzdem Buch 7.

Deshalb verhalten sich die beiden Methoden auch unterschiedlich, wenn sie wiederholt werden. Zwei identische Anfragen POST /books legen zwei Bücher mit zwei IDs an. Zwei identische Anfragen PUT /books/7 hinterlassen Buch 7 genau so wie eine. Lektion 6 macht aus diesem Unterschied Tests.

erlaubt PUT, eine Ressource unter einer URL anzulegen, die noch nichts benennt (RFC 9110, Abschnitt 9.3.4). Diese API tut das nicht: IDs vergibt der Server, also bekommt ein PUT oder DELETE an eine ID ohne Buch 404.

Eine andere API könnte auf ein DELETE mit 200 OK und dem gelöschten Buch im Body antworten, und beide Entwürfe sind gültiges HTTP. Diese sendet 204: Das Buch ist weg, und es gibt nichts zu senden. Wichtig ist, dass die API sagt, was sie tut, und SERVICE_CONTRACT.md sagt es.

Derselbe Speicher, von Hand

Der Zustand, den dein handle bekommt, ist ein von Büchern mit der ID als Schlüssel, dazu next_id. Führ das hier aus, um die drei Schreibzugriffe als Dictionary-Operationen zu sehen, mit den Entscheidungen des Servers ausgegeben:

Try it

Das PUT hat das "id": 99 des Bodys ignoriert, und das Löschen von Buch 1 hat next_id nicht verändert: Das nächste POST bekommt 3, also wird eine gelöschte ID nie wieder vergeben.

Welche Anfrage fügt ein Buch hinzu, dessen ID der Server wählt?

Was gibt das aus?

books = {1: "Kindred", 2: "Dune"}
next_id = 3
books[next_id] = "Piranesi"
next_id += 1
books[2] = "Dune (revised)"
books.pop(1)
print(books, next_id)

Was sendet ein erfolgreiches DELETE /books/7 in dieser API?

In der nächsten Lektion schreibst du die drei , die diese Tabelle wahr machen.