Bodies van verzoeken en een JSON-API
Resources en collecties
Je server kan nu de body van een verzoek lezen en controleren dat die
Een URL geeft een ding een naam
In hoofdstuk 2 zag je het idee dat een GET vraagt erom en is veilig: het verandert niets. PUT en DELETE zijn POST is geen van beide.
De leeslijst heeft twee soorten resources:
De collectie,
/books: devan alle boeken. Een item,
/books/7: één boek, aangeduid met zijn id.
Een schrijfactie gaat óf naar de collectie, om er een boek aan toe te voegen, óf naar één item, om dat boek te wijzigen of te verwijderen. Dat geeft vier bewerkingen, vaak CRUD genoemd: create, read, update, delete (aanmaken, lezen, bijwerken, verwijderen). Zo koppelt deze API ze:
| Verzoek | Effect | Succes | Body van het antwoord |
|---|---|---|---|
POST /books | voegt een boek toe op een id die de server kiest | 201 Created, met location: /books/{id} | het opgeslagen boek |
GET /books | geen | 200 OK | {"books": [...]} |
GET /books/{id} | geen | 200 OK | het boek |
PUT /books/{id} | vervangt dat boek door de body | 200 OK | het opgeslagen boek |
DELETE /books/{id} | verwijdert dat boek | 204 No Content | geen |
Getekend als een boekenplank ziet dezelfde tabel er zo uit:
/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
Elke andere methode op een van beide paden krijgt nog steeds 405, waarbij allow nu opsomt wat de tabel toestaat: GET, HEAD, POST op de collectie en GET, HEAD, PUT, DELETE op een item. Een PUT /books zou de hele collectie vervangen, en een DELETE /books zou hem leegmaken; deze API biedt geen van beide.
201 Created en 204 No Content zijn allebei 2xx-successen, dus een client die eerst op klasse beslist, zoals de jouwe sinds hoofdstuk 3 doet, behandelt ze al als succes. Les 4 kijkt naar wat elk van beide op de verbinding vereist.
Wie kiest de id
Bij POST kiest de server. De client stuurt een titel en een auteur naar /books; de server geeft het boek de volgende vrije id en vertelt de client waar het nu staat, in de header location. De client kent de id niet voordat hij het vraagt, en daarom heeft POST /books/7 hier geen zin.
Bij PUT kiest de URL. PUT /books/7 zegt “het boek op /books/7 moet nu dit zijn”, dus de id komt uit het pad en nooit uit de body: een body die "id": 99 zegt, vervangt nog steeds boek 7.
Daarom gedragen de twee methoden zich ook anders als ze worden herhaald. Twee identieke POST /books-verzoeken maken twee boeken met twee id’s. Twee identieke PUT /books/7-verzoeken laten boek 7 precies zo achter als één verzoek deed. Les 6 maakt van dat verschil tests.
PUT een resource aanmaakt op een URL die nog niets aanduidt (RFC 9110, sectie 9.3.4). Deze API doet dat niet: id’s worden door de server uitgedeeld, dus een PUT of DELETE naar een id waar geen boek staat, krijgt 404.
Een andere API zou een DELETE kunnen beantwoorden met 200 OK en het verwijderde boek in de body, en beide ontwerpen zijn geldige HTTP. Deze stuurt 204: het boek is weg, en er is niets te versturen. Waar het om gaat, is dat de API zegt wat hij doet, en SERVICE_CONTRACT.md zegt dat.
Dezelfde opslag, met de hand
De state die je handle ontvangt, is een next_id. Voer dit uit om de drie schrijfacties als dict-bewerkingen te zien, met de keuzes van de server afgedrukt:
De PUT negeerde de "id": 99 uit de body, en het verwijderen van boek 1 veranderde next_id niet: de volgende POST krijgt 3, dus een verwijderde id wordt nooit opnieuw uitgedeeld.
Welk verzoek voegt een boek toe waarvan de server de id kiest?
Wat drukt dit af?
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)
Wat stuurt een geslaagde DELETE /books/7 in deze API?
In de volgende les schrijf je de drie