skip to content

HttpMessageConverters

HttpMessageConverter is the SPI that reads and writes bodies, and the ordered list of converters plus canRead/canWrite decides which one handles a given media type. Knowing this explains both how JSON appears and how to add a custom format.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is an HttpMessageConverter in Spring MVC, and when does Spring use one?

level: juniorimportance: must knowfreq 70%

answer

  1. body <-> object SPI
  2. @RequestBody read / @ResponseBody write
  3. canRead/canWrite = type + media type
  4. conditional on classpath (Jackson=JSON)
  5. view-returning @Controller does NOT use converters

basics

~10 s

It converts between HTTP request/response bodies (bytes) and Java objects. Spring uses it whenever you annotate a parameter with @RequestBody or a return value with @ResponseBody (including @RestController).

solid answer

~40 s

An HttpMessageConverter is Spring's SPI for turning an HTTP message body into a Java object (read) and a Java object back into a body (write). It powers @RequestBody, @ResponseBody, HttpEntity, and ResponseEntity — everything a @RestController produces or consumes. Each converter declares which Java types and media types it handles via canRead(Class, MediaType) and canWrite(Class, MediaType). For example, MappingJackson2HttpMessageConverter maps JSON <-> objects, StringHttpMessageConverter handles plain text, and ByteArrayHttpMessageConverter handles byte[]. When a controller method returns an object, Spring picks the first registered converter whose canWrite matches both the object's type and the negotiated response content type, then calls write() to serialize it. Reading works symmetrically using the request's Content-Type.

code

java · 20 lines
java
@RestController
public class UserController {

    // @RequestBody: MappingJackson2HttpMessageConverter READS the JSON body
    // into a UserDto (request Content-Type: application/json).
    // Return value: the same converter WRITES the UserDto back as JSON
    // because @RestController implies @ResponseBody and the client
    // sent Accept: application/json.
    @PostMapping("/users")
    public UserDto create(@RequestBody UserDto in) {
        return in.withId(42L);
    }

    // Returns raw text -> StringHttpMessageConverter writes text/plain,
    // NOT JSON. A common surprise.
    @GetMapping("/ping")
    public String ping() {
        return "pong";
    }
}

go deeper

for a junior

Know it converts body <-> object and is triggered by @RequestBody / @ResponseBody.

for a middle

Name the main built-in converters and know they're registered conditionally on the classpath.

for a senior

Explain the read/write SPI (canRead/canWrite by type + media type) and that view returns bypass converters.

for a principal

Contrast message conversion (whole body) with Converter/Formatter (single value), and understand the argument-resolver / return-value-handler machinery that drives it.

## What it is `HttpMessageConverter<T>` is a strategy interface (an SPI — service provider interface) in `org.springframework.http.converter`. Its job is **body serialization**: converting the raw bytes of an HTTP message body to and from Java objects. It is distinct from `Converter`/`Formatter` (which convert individual values like a String path variable to an `int`) — an `HttpMessageConverter` deals with the **whole body** of a request or response. ## The interface ```java public interface HttpMessageConverter<T> { boolean canRead(Class<?> clazz, MediaType mediaType); boolean canWrite(Class<?> clazz, MediaType mediaType); List<MediaType> getSupportedMediaTypes(); T read(Class<? extends T> clazz, HttpInputMessage inputMessage); void write(T t, MediaType contentType, HttpOutputMessage outputMessage); } ``` - **read** = deserialize the request body into a Java object. - **write** = serialize a Java object into the response body. - **canRead / canWrite** = ask the converter whether it can handle a given Java type AND a given media type (MIME type such as `application/json`). ## When Spring invokes them Converters are triggered by the argument resolvers / return-value handlers for these annotations and types: - `@RequestBody` on a parameter → **read** the request body into that parameter (handled by `RequestResponseBodyMethodProcessor`). - `@ResponseBody` on a method / class-level `@RestController` → **write** the return value as the response body. - `HttpEntity<T>` parameter → read; `ResponseEntity<T>` / `HttpEntity<T>` return → write (handled by `HttpEntityMethodProcessor`). Plain `@Controller` methods that return a **view name** do NOT use message converters — those go through the view resolution / `ViewResolver` path instead. Message conversion is specifically the `@RestController` / REST-body path. ## Default converters Spring MVC registers a default list (via `WebMvcConfigurationSupport.addDefaultHttpMessageConverters`). Common ones: - `ByteArrayHttpMessageConverter` — `byte[]` - `StringHttpMessageConverter` — `String` (text) - `ResourceHttpMessageConverter` / `ResourceRegionHttpMessageConverter` — `Resource` (files, range requests) - `AllEncompassingFormHttpMessageConverter` — form data (`application/x-www-form-urlencoded`, `multipart/form-data`) - `MappingJackson2HttpMessageConverter` — JSON (only if Jackson is on the classpath) Many are registered **conditionally** based on what libraries are present on the classpath (Jackson for JSON, JAXB for XML, etc.). ## Gotcha Because selection is media-type + type based, forgetting a `Content-Type` header on a request or an `Accept` header on the client can change which converter is chosen. And returning a raw `String` from a `@RestController` produces `text/plain` via `StringHttpMessageConverter`, not JSON — a frequent surprise.

  • Does a @Controller method returning a view name use an HttpMessageConverter?
    No. View-name returns go through view resolution (ViewResolver). Message converters are used only for body-producing paths: @ResponseBody / @RestController / ResponseEntity / HttpEntity.
  • Why might a @RestController method return text/plain instead of JSON?
    Because it returns a raw String, and StringHttpMessageConverter (which handles String + text/plain and */*) is selected before or instead of Jackson. To force JSON, wrap the value in an object/DTO or set produces = application/json.

context

open as a page

How does Spring select which HttpMessageConverter to use for a given request or response? Explain canRead/canWrite, ordering, and content negotiation.

level: seniorimportance: must knowfreq 60%

basics

~20 s

Spring iterates the ordered converter list and picks the first whose canRead/canWrite returns true for both the Java type and the media type. For writing, content negotiation (the Accept header) determines the target media type; for reading it's the request's Content-Type.

open as a page

Name the main built-in HttpMessageConverters and what Java type / media type each handles.

level: middleimportance: should knowfreq 55%

basics

~10 s

ByteArrayHttpMessageConverter (byte[]), StringHttpMessageConverter (String / text/plain), MappingJackson2HttpMessageConverter (JSON), and FormHttpMessageConverter (form-urlencoded / multipart into a MultiValueMap). Jackson is only registered if it's on the classpath.

open as a page

How do you add, remove, reorder, or reconfigure HttpMessageConverters? Contrast configureMessageConverters with extendMessageConverters.

level: seniorimportance: should knowfreq 45%

basics

~20 s

Implement WebMvcConfigurer. Override extendMessageConverters(list) to tweak the existing defaults (add, remove, reorder) while keeping them. Override configureMessageConverters(list) only if you want to fully REPLACE the defaults — if that list is non-empty, Spring adds no defaults.

open as a page

As a principal engineer, discuss non-obvious HttpMessageConverter behaviors: the StringHttpMessageConverter charset default, converter reuse across RestTemplate, and how the write-side producible-type computation can surprise you.

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Key gotchas: StringHttpMessageConverter defaults to ISO-8859-1 when no charset is given (garbling UTF-8 text); the same converter SPI is reused by RestTemplate on the client side; and because String and byte[] converters advertise /, they can quietly pre-empt Jackson depending on ordering and Accept.

open as a page