2026-09-11 · 8 min read
- json
- http
- api-design
HTTP response handling has two layers: interpreting the status and metadata, then decoding a representation when one exists. A JSON client that immediately calls a parser for every successful response collapses those layers and fails on legitimate bodyless responses.
HTTP 204 No Content cannot contain response content. HEAD returns the headers a corresponding GET would send but no response body. Other statuses may have content according to their contract, including structured error documents that deserve different handling from successful data.
Model transport outcomes before domain data
Preserve status, headers, final URL, and cancellation state until the caller has enough information to select a decoder. Represent no-content success explicitly instead of inventing an empty object, empty array, or null JSON document that the server never sent.
- Treat 204 and HEAD as bodyless regardless of a misleading Content-Type.
- Define whether 205 Reset Content maps to the same application result.
- Handle 304 within cache semantics rather than as an ordinary API body.
- Keep an empty 200 distinct from a valid JSON null payload.
Select decoders from the endpoint contract
An endpoint schema should identify which statuses return JSON and the shape for each. Generated clients and shared wrappers should branch on that schema and status before reading the stream, while still allowing text, binary, or problem-detail responses where documented.
Content-Type is useful validation but is not sufficient by itself. A server may emit a stale header on 204, and an intermediary may replace an error body with HTML. Compare the actual response with the declared contract and produce an error that preserves safe diagnostic context.
Keep malformed and absent content separate
Do not catch every JSON syntax exception and return an empty value. Zero bytes where JSON was promised, a truncated transfer, invalid encoding, and malformed syntax are operationally different failures and should remain visible to telemetry and callers.
- Cap response size before buffering.
- Propagate abort and timeout causes.
- Avoid logging full payloads or credentials.
- Record status, media type, byte count, and request correlation safely.
Test clients through the full HTTP path
Use Flashman's JSON formatter for synthetic documents, diff for decoded result models, URL tool for public request targets, timestamp converter for sanitized timelines, and Base64 for disposable binary fixtures. These tools inspect representations; they do not replace protocol tests.
Exercise 200 with object, array, null, whitespace, empty, and malformed bodies; 201 and 202 contracts; 204 and 205; HEAD; 304 through a cache; redirects; compressed and chunked responses; proxy-generated HTML errors; aborts; retries; and each browser, mobile, and server client implementation.