HTTP and JSON
Build a small HTTP service with clear boundaries, tests, logs and a repeatable runtime.
THE IDEA
A request and a response are just text
HTTP is a conversation of two messages. The client sends a request: a method (what to do), a path (to which resource), headers (facts about the message) and sometimes a body. The server sends back a response: a status code (how it went), headers and a body. Edit the request below and press Send. The panel on the right shows the exact text of both messages.
You edit the request
Headers (one per line)
Body
On the wire: request
POST /items HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Content-Length: 30
{"name": "ruler", "price": 90}On the wire: response
(press Send)
The server is a small simulation written for this course, with its own rules (for example it answers 422 for a well-formed JSON body that fails validation). Real servers choose their own.
Status codes are the headline
- 2xx worked. 200 OK, 201 Created (a new resource now exists), 204 No Content (done, nothing to send back).
- 4xx the request has a problem the caller can fix. 400 Bad Request, 404 Not Found, 405 Method Not Allowed, 415 Unsupported Media Type.
- 5xx the server failed.
Which 4xx code to use for a validation failure is a design choice. The simulated server in this lesson answers 400 when the body is not JSON at all and 422 when it is JSON but the values are not acceptable.
JSON has types, and strings are not numbers
JSON text has a few kinds of value: string, number, boolean, null, array and object. The text "12" is a string and 12 is a number. A parser faithfully hands you whichever one was sent, and it is validation that decides what your program accepts. JSON that parses is not necessarily JSON your application can use.
| key | JSON type | value |
|---|---|---|
| name | string | "ruler" |
| price | string | "90" |
| count | number | 3 |
| paid | boolean | false |
| note | null | null |
| tags | array | ["a","b"] |
A value in quotes is a string, even if it looks like a number. Parsing succeeds either way. Deciding whether "90" is acceptable where a number is expected is the job of validation.
Predict first. Is the JSON string "12" automatically the same as the number 12?
Chapter context
Engineering makes a program understandable when it fails or changes. Separate pure calculations from external I/O so behavior can be checked cheaply and errors can be connected to specific inputs.
TRY IT YOURSELF
This is a simulation: curl here understands only -X, -H, -d, -i and -s, and talks to a small make-believe server at localhost:8000. Real curl has many more options.
- List all items (a plain GET of /items)
- Fetch the notebook, item 2, on its own
- Ask for an item that does not exist and read the status
- Send a body without saying it is JSON and watch the server refuse it
- Send valid JSON whose price is the string "12" instead of a number and read the validation error
- Create a new item with a correct JSON request and get 201 Created
- Fetch the item you just created by its id
- Send a DELETE to the whole collection with -i and read the Allow header
- Delete the item you created and get 204 No Content
Last requests the server saw
Nothing yet. This is the simulated server at localhost:8000.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Is JSON string "12" automatically the same as number 12?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
For money examples, use integer minor units rather than binary floating point. The subtotal is sum(quantity × unit_price), and the domain must specify rounding and tax rules before those operations are implemented.
Worked example
Two items cost 125 minor units each and three cost 40 each. The subtotal is 2×125 + 3×40 = 370. A test can exercise this calculation without starting an HTTP server. Request parsing and response formatting need separate checks.
Reference code
The following example uses the Python standard library and does not call a remote service.
def subtotal(items):
return sum(quantity * price for quantity, price in items)
assert subtotal([(2, 125), (3, 40)]) == 370
assert subtotal([]) == 0
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
Test coverage counts executed lines, not verified requirements. A container does not turn an unsafe operation into a safe one. JSON validity alone does not establish application validity.