3.0 KiB
Request Body
req.body() returns a RequestBody — a lazy accessor that exposes the HTTP request body in
two mutually exclusive modes. Choose one per handler based on the payload size and how you
intend to consume it.
Two modes
bytes() — materialise
byte[] body = req.body().bytes();
Reads the full body into a byte[] and caches the result. Safe to call multiple times — the
second call returns the same cached array, no I/O is performed again.
Use for:
- JSON parsing (
new String(body, UTF_8)then parse) - Small form data
- Any payload that must be inspected in full before responding
Limit: throws IllegalStateException if Content-Length exceeds Integer.MAX_VALUE
(~2 GB). For larger bodies use stream().
Chunked bodies: reads until ChunkedInputStream signals EOF, then caches.
stream() — zero-copy
InputStream in = req.body().stream();
Returns a bounded InputStream without allocating the full body upfront.
Use for:
- Large file uploads
- Bodies passed directly to disk, a database, or another stream
- Multipart parsing (via
Multipart.of(req)which calls this internally)
Fixed-length bodies: a SequenceInputStream of any pre-buffered header bytes (from the
RequestParser read buffer, already in memory) followed by a bounded view of the socket
stream. The small pre-buffered slice costs nothing extra.
Chunked bodies: returns the raw ChunkedInputStream directly. It de-chunks on the fly —
reads the hex chunk-size line, the data, and the trailing CRLF — and signals EOF at the end
of the last chunk, leaving the socket positioned correctly for the next keep-alive request.
After bytes(): if the body was already materialised, stream() returns a fresh
ByteArrayInputStream over the cached array. No I/O is performed.
Mutual exclusivity
// SAFE — pick one mode
byte[] b = req.body().bytes();
InputStream s = req.body().stream();
// UNSAFE — bytes() then stream() is safe (stream() wraps the cache)
// UNSAFE — stream() partially read, then bytes() → undefined results
The rule: if you call stream() first and partially read it, calling bytes() afterwards
will either throw (if contentLength > 2 GB) or read only the remaining socket bytes into
the array, missing the already-consumed portion.
Checking the size
long size = req.body().contentLength();
- > 0 — fixed-length body; exact byte count
- == 0 — empty body (
Content-Length: 0or no body present) - == -1 —
Transfer-Encoding: chunked; size is unknown upfront
boolean empty = req.body().isEmpty(); // true when contentLength == 0
Decision guide
| Payload size | Encoding | Use |
|---|---|---|
| < 2 GB, fully needed | any | bytes() |
| Any size, pass-through | any | stream() |
| Multipart form | any | Multipart.of(req) (uses stream() internally) |
| Size unknown | chunked | stream() |