skip to content

Explain BodyPublishers and BodyHandlers: what each is for, and give examples of common ones.

level: middleimportance: should knowfreq 55%

answer

  1. BodyPublisher = outgoing request body source
  2. BodyHandler = how the incoming response body is delivered + its Java type
  3. Publishers: ofString/ofByteArray/ofFile/ofInputStream/noBody
  4. Handlers: ofString/ofByteArray/ofFile/ofLines/ofInputStream/discarding
  5. ofString buffers all; ofFile/ofInputStream stream (memory)

basics

~20 s

A BodyPublisher supplies the request body you send (e.g. a string or file). A BodyHandler decides how the response body is delivered to you (e.g. as a String, byte array, or written to a file). They are opposite ends: outgoing vs incoming body.

solid answer

~50 s

BodyPublishers produce the bytes of the *request* body. Factory methods on HttpRequest.BodyPublishers include ofString(s), ofByteArray(b), ofFile(path), ofInputStream(supplier), and noBody(). You pass one to POST/PUT/method. BodyHandlers decide how the *response* body is consumed and what Java type body() yields: HttpResponse.BodyHandlers.ofString() → String, ofByteArray() → byte[], ofFile(path) → Path (streamed to disk, good for large downloads), ofLines() → Stream<String>, ofInputStream() → InputStream, and discarding() to drop the body. The handler is a factory that, given the response status/headers, returns a BodySubscriber that actually receives the bytes — this is why ofFile can stream without buffering everything in memory. Choosing the right handler matters for memory: ofString loads the whole body into a String, while ofFile/ofInputStream stream. Both publishers and handlers are pluggable, so the same request shape can post different sources and the same response can be delivered different ways.

go deeper

for a junior

Knows a publisher sends the request body and a handler shapes the response body; can use ofString for both directions.

for a middle

Lists common publishers/handlers and explains the memory difference between buffering (ofString) and streaming (ofFile/ofInputStream).

for a senior

Understands the handler-as-factory over ResponseInfo, the reactive Flow underpinning, and chooses handlers based on size, status, and resource cleanup.

for a principal

Designs custom BodySubscribers/publishers for backpressure-sensitive integrations and standardizes safe handler choices to prevent memory blowups across services.

## The two directions of a body An HTTP message can carry a **body** in both directions: the *request* may send one (e.g. JSON in a POST), and the *response* usually returns one (the data you asked for). `java.net.http` gives each direction its own pluggable abstraction. ## `BodyPublisher` — produces the REQUEST body A **`HttpRequest.BodyPublisher`** is the source of the outgoing body's bytes. You attach one when the method has a body: ```java HttpRequest req = HttpRequest.newBuilder(uri) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"ann\"}")) .build(); ``` Common factories on `HttpRequest.BodyPublishers`: - `ofString(String)` — text body (optionally with a charset), - `ofByteArray(byte[])` — raw bytes, - `ofFile(Path)` — stream a file as the body (doesn't load it all into memory), - `ofInputStream(Supplier<InputStream>)` — stream from an arbitrary source (the supplier lets it re-open on retry), - `noBody()` — an explicitly empty body. Under the hood a `BodyPublisher` is a *reactive* `Flow.Publisher<ByteBuffer>`: it publishes the body bytes to the client on demand. That reactive nature is what lets large bodies stream with backpressure instead of being buffered whole. ## `BodyHandler` — shapes the RESPONSE body A **`HttpResponse.BodyHandler<T>`** decides *how the incoming response body is consumed* and *what Java type* `response.body()` returns. You pass it to `send`/`sendAsync`: ```java HttpResponse<String> r = client.send(req, HttpResponse.BodyHandlers.ofString()); ``` Common factories on `HttpResponse.BodyHandlers`: - `ofString()` → `HttpResponse<String>` (loads the full body into a String), - `ofByteArray()` → `byte[]`, - `ofFile(Path)` → `Path` (streams the body straight to a file — ideal for large downloads), - `ofLines()` → `Stream<String>` (lazily, line by line), - `ofInputStream()` → `InputStream` (you read it yourself; remember to close it), - `discarding()` → drops the body (you only care about status/headers), - `ofPublisher()` → exposes the body as a reactive `Flow.Publisher`. ### Why a handler is a *factory*, not just a sink A `BodyHandler<T>` is functionally `(ResponseInfo) -> BodySubscriber<T>`. When the response's status and headers arrive, the handler is invoked to build a **`BodySubscriber`** that actually receives the body bytes. This two-step design means the handler can *inspect the status first* and choose how to consume the body — e.g. discard the body on a 500, or stream to a file on 200. The `BodySubscriber` side is a reactive `Flow.Subscriber<List<ByteBuffer>>`, mirroring the publisher. ## Memory implications (the practical takeaway) - `ofString` / `ofByteArray` **buffer the entire body in memory** — fine for small responses, dangerous for large ones. - `ofFile`, `ofInputStream`, `ofLines`, `ofPublisher` **stream**, so memory stays bounded — use these for large or unbounded downloads. ## Symmetry Publisher : request body :: Handler/Subscriber : response body. Both sit on Java's reactive `Flow` API, giving streaming and backpressure in both directions. Because they're pluggable, the same `HttpRequest` shape can post a string, a file, or a stream, and the same call can deliver the reply as a String, a file, or a lazy line stream — just by swapping the factory.

  • Why is a BodyHandler defined as a function of ResponseInfo rather than a plain consumer?
    So it can see the status code and headers first and then choose how to consume the body — e.g. discard on error, stream to a file on success — returning the appropriate BodySubscriber.
  • Which handler should you use to download a large file, and why?
    BodyHandlers.ofFile(path): it streams the body directly to disk via a BodySubscriber, keeping memory bounded, instead of buffering the whole body like ofString/ofByteArray.

saying these in an interview costs you the question

  • Swapping the roles — using a BodyHandler to send a request body or a BodyPublisher to read a response
  • Using ofString() for huge downloads and risking OutOfMemoryError instead of ofFile/ofInputStream
  • Forgetting to close the InputStream from ofInputStream()
  • Not realizing a BodyHandler can inspect status/headers before deciding how to consume the body

context