skip to content

In Spring WebFlux, what are Encoder/Decoder and HttpMessageReader/HttpMessageWriter, and what do they do?

level: juniorimportance: must knowfreq 55%

answer

  1. Encoder=objects->bytes, Decoder=bytes->objects
  2. Reader/Writer add HTTP concerns
  3. EncoderHttpMessageWriter wraps Encoder
  4. Everything is Flux<DataBuffer>
  5. Jackson2JsonEncoder/Decoder = JSON pair

basics

~10 s

They convert between Java objects and the raw bytes of an HTTP body. A Decoder/HttpMessageReader turns request bytes into objects; an Encoder/HttpMessageWriter turns response objects back into bytes (e.g. JSON).

solid answer

~40 s

WebFlux replaces MVC's HttpMessageConverter with two reactive abstractions. Encoder<T> serializes a stream of objects into bytes; Decoder<T> deserializes bytes into objects. These are content-only, unaware of HTTP. HttpMessageWriter and HttpMessageReader wrap them and add HTTP concerns like Content-Type negotiation and headers. The standard adapters are EncoderHttpMessageWriter and DecoderHttpMessageReader, so registering a codec usually means registering an Encoder/Decoder pair. Everything works on Reactive Streams Publishers (Flux/Mono) of DataBuffer rather than blocking byte arrays, so bodies can be processed as they stream in and out. Jackson2JsonEncoder/Jackson2JsonDecoder are the built-in JSON pair; there are also codecs for plain strings, form data, multipart, and byte buffers.

code

java · 19 lines
java
// Content-only layer
public interface Encoder<T> {
    Flux<DataBuffer> encode(Publisher<? extends T> inputStream,
                            DataBufferFactory bufferFactory,
                            ResolvableType elementType,
                            MimeType mimeType,
                            Map<String, Object> hints);
}

public interface Decoder<T> {
    Flux<T> decode(Publisher<DataBuffer> inputStream,
                   ResolvableType elementType,
                   MimeType mimeType,
                   Map<String, Object> hints);
}

// HTTP-aware wrappers used at runtime:
//   EncoderHttpMessageWriter  wraps an Encoder<T>
//   DecoderHttpMessageReader  wraps a Decoder<T>

go deeper

for a junior

Know the direction: Decoder/Reader = incoming bytes to objects, Encoder/Writer = objects to outgoing bytes, and that JSON uses Jackson.

for a middle

Explain the two layers (content Encoder/Decoder vs HTTP Reader/Writer) and that they stream DataBuffers reactively.

for a senior

Contrast with MVC's HttpMessageConverter and explain MIME/type-based selection plus the EncoderHttpMessageWriter/DecoderHttpMessageReader wrapping.

for a principal

Discuss streaming vs aggregation semantics, reference-counted DataBuffer lifecycle, and how codec selection interacts with content negotiation across server and WebClient.

## The problem they solve An HTTP body on the wire is just bytes. A controller wants to work with Java objects (a `User`, a `List<Order>`). Something must translate between the two. In blocking **Spring MVC** that job belongs to `HttpMessageConverter`. In reactive **Spring WebFlux** the same job is split across a small layered set of interfaces designed to work with streaming, non-blocking data. ## The two layers **1. Content codecs (HTTP-agnostic):** - `Encoder<T>` — takes a `Publisher<? extends T>` (a stream of objects) and produces a `Flux<DataBuffer>` (a stream of byte chunks). Used to write responses / request bodies. - `Decoder<T>` — takes a `Publisher<DataBuffer>` and produces a `Flux<T>` (objects). Used to read request / response bodies. These know nothing about HTTP headers or status codes — only how to turn objects into bytes and back for a given `MimeType`. **2. Message codecs (HTTP-aware):** - `HttpMessageWriter<T>` — writes a `Publisher<T>` to a `ReactiveHttpOutputMessage`, choosing the `Content-Type`, setting `Content-Length`, etc. - `HttpMessageReader<T>` — reads a `ReactiveHttpInputMessage` into a `Flux<T>` / `Mono<T>`, checking the request's `Content-Type`. The glue: `EncoderHttpMessageWriter` wraps an `Encoder`, and `DecoderHttpMessageReader` wraps a `Decoder`. So most of the time you supply an `Encoder`/`Decoder` and Spring wraps it into the HTTP-aware layer automatically. ## DataBuffer `DataBuffer` is Spring's abstraction over a chunk of bytes (it wraps a Netty `ByteBuf` or a `java.nio.ByteBuffer` depending on the runtime). Because bodies are streams of `DataBuffer`, WebFlux can process data incrementally without buffering the whole payload into a `byte[]` — the key difference from MVC. ## Built-in codecs - **JSON:** `Jackson2JsonEncoder` / `Jackson2JsonDecoder` (Jackson-backed). - **Strings:** `CharSequenceEncoder` / `StringDecoder`. - **Bytes / resources:** `ByteArrayEncoder`, `ByteBufferEncoder`, `DataBufferEncoder`, `ResourceEncoder`/`ResourceDecoder`. - **Form & multipart:** `FormHttpMessageReader`/`Writer`, multipart readers. - (Optional) protobuf, Jackson Smile, XML (JAXB/Jackson) codecs. ## Where they live On the server the active set is held by a `ServerCodecConfigurer`; on the client (`WebClient`) by a `ClientCodecConfigurer`. You customize them via `WebFluxConfigurer.configureHttpMessageCodecs(...)` or `WebClient.Builder.codecs(...)`. ## Gotchas - Codecs are selected by **MIME type** and target **type** — if none matches you get `415 Unsupported Media Type` (read) or `406 Not Acceptable` / no writer found. - With streaming JSON of multiple elements, WebFlux uses **NDJSON / `application/stream+json`** semantics for true streaming; a plain `application/json` array is aggregated. - Because Netty `DataBuffer`s are reference-counted, low-level custom decoders must **release** buffers (`DataBufferUtils.release`) to avoid leaks — the built-in codecs handle this for you.

  • What is the equivalent abstraction in Spring MVC, and why can't WebFlux reuse it?
    Spring MVC uses HttpMessageConverter, which reads/writes the whole body from blocking InputStream/OutputStream (byte[]). WebFlux is non-blocking and stream-based, so it needs codecs that operate on Publisher<DataBuffer> instead.
  • How does WebFlux decide which codec to use for a given request or response?
    By matching the message's Content-Type (or the request's Accept header for writing) against each codec's supported MimeTypes, plus checking the target Java type it can handle. First matching reader/writer wins; no match yields 415 or 406.

saying these in an interview costs you the question

  • Saying WebFlux uses HttpMessageConverter like MVC
  • Claiming Encoder/Decoder handle HTTP headers/status (that's the Reader/Writer layer)
  • Thinking bodies are always buffered into a byte[] before decoding

context