Skip to content
Spring Boot HTTP Message Conversion & Content Negotiation Deep Dive

Spring Boot HTTP Message Conversion & Content Negotiation Deep Dive

This article explains Spring Boot's HttpMessageConverter and Content Negotiation mechanisms, covering default converters, source code flow, customization via WebMvcConfigurer, and troubleshooting 415/406 errors.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop

1. What Message Converters Do in the Request Pipeline

When a controller method uses @RequestBody or @ResponseBody, Spring needs to convert between HTTP message bodies and Java objects. Two mechanisms work together:

HttpMessageConverter : Handles reading (deserialization) and writing (serialization) for specific media types and Java types.

Content Negotiation : Decides which media type to use for the response based on the request.

The converter does not decide the format; it only knows whether it can handle a given media type and Java type. The format decision is made by content negotiation.

2. HttpMessageConverter Interface and Default Converters

2.1 Core Interface

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) throws IOException, HttpMessageNotReadableException;
    void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}

The adapter checks canRead / canWrite first, then calls read / write.

2.2 Spring Boot Default Converters (registered based on classpath dependencies)

ByteArrayHttpMessageConverter

– byte arrays,

application/octet-stream
StringHttpMessageConverter

– strings, text/plain,

*/*
ResourceHttpMessageConverter

– file resources,

application/octet-stream
MappingJackson2HttpMessageConverter

– JSON (Jackson),

application/json
MappingJackson2XmlHttpMessageConverter

– XML (Jackson XML),

application/xml
FormHttpMessageConverter

– form data,

application/x-www-form-urlencoded
AllEncompassingFormHttpMessageConverter

– form + multipart, multipart/form-data Order matters: the adapter iterates the list and uses the first converter whose canWrite returns true.

2.3 JSON Converter and ObjectMapper

MappingJackson2HttpMessageConverter

holds an ObjectMapper. Spring Boot auto-configuration injects the globally configured ObjectMapper (via spring.jackson.* properties) into the converter. Thus date formats, null handling, and serialization rules are controlled through that shared ObjectMapper.

2.4 Converter List Source

RequestMappingHandlerAdapter

holds the converter list, provided by MVC configuration. WebMvcAutoConfiguration registers defaults based on classpath; custom converters added via WebMvcConfigurer are merged into the same list.

3. Content Negotiation: How the Response Type Is Determined

3.1 Problem

The same return object can be serialized as JSON or XML. The server must pick a media type both sides accept and set the Content-Type header.

3.2 Three Negotiation Strategies

ContentNegotiationManager

aggregates multiple ContentNegotiationStrategy implementations, tried in order:

HeaderContentNegotiationStrategy – uses Accept header (default). Parses q-values for priority.

ParameterContentNegotiationStrategy – uses a request parameter (e.g., ?format=json). Usually higher priority than Accept header.

PathExtensionContentNegotiationStrategy – uses URL suffix (e.g., /order.json). Deprecated/weakened since Spring Boot 2.x (PathPatternParser disables suffix matching by default).

3.3 Negotiation Flow

1. Collect producible media types from the handler method (produces / converter support)
2. Invoke each strategy to get client-preferred types
3. Intersect: server producible ∩ client acceptable
4. Select best MediaType, pass to converter.write
5. No match → 406 Not Acceptable

3.4 Accept Header (Default)

Accept: application/json;q=0.9, application/xml;q=0.8

Server prefers JSON; falls back to XML if JSON not supported.

3.5 Request Parameter

Enable via config:

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: format
      favor-path-extension: false

Then GET /order/1?format=xml selects XML converter.

3.6 Suffix Strategy Evolution

Early versions allowed /order/1.json. Since Spring Boot 2.x, PathPatternParser disables suffix matching by default; PathExtensionContentNegotiationStrategy is discouraged. Prefer Accept header or parameter.

3.7 produces and consumes Direct Locking

@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
consumes

: restricts acceptable request media types; mismatch → 415 Unsupported Media Type . produces: restricts producible response types; negotiation failure → 406 Not Acceptable .

4. Source Code Flow: From @RequestBody to write

4.1 Argument Resolution: RequestResponseBodyMethodProcessor

@RequestBody

is resolved by RequestResponseBodyMethodProcessor (extends AbstractMessageConverterMethodArgumentResolver). Its resolveArgument calls readWithMessageConverters:

protected <T> Object readWithMessageConverters(..., MethodParameter parameter, Type targetType) {
    MediaType contentType = inputMessage.getHeaders().getContentType();
    for (HttpMessageConverter<?> converter : this.messageConverters) {
        if (converter.canRead(targetClass, contentType)) {
            return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
        }
    }
    throw new HttpMediaTypeNotSupportedException(contentType, ...);
}

Iterates converters, picks first matching canRead, calls read. Throws 415 if none.

4.2 Return Value Handling: Same Processor

The same class implements HandlerMethodReturnValueHandler for @ResponseBody:

public void handleReturnValue(Object returnValue, MethodParameter returnType, ...) {
    List<MediaType> producibleTypes = getProducibleMediaTypes(...);
    MediaType selected = contentNegotiationManager.resolveMediaTypes(..., producibleTypes).get(0);
    for (HttpMessageConverter<?> converter : this.messageConverters) {
        if (converter.canWrite(returnClass, selectedMediaType)) {
            converter.write(returnValue, selectedMediaType, outputMessage);
            return;
        }
    }
    throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}

Steps: 1) content negotiation to pick MediaType, 2) find first converter with canWrite, 3) write and set Content-Type. Throws 406 if none.

4.3 Key Source Classes

HttpMessageConverter

– top-level interface MappingJackson2HttpMessageConverter – JSON read/write StringHttpMessageConverter – string read/write RequestResponseBodyMethodProcessor – resolves @RequestBody, handles

@ResponseBody
ContentNegotiationManager

– aggregates strategies HeaderContentNegotiationStrategy – Accept header based ParameterContentNegotiationStrategy – parameter based WebMvcAutoConfiguration – Spring Boot default wiring

5. Customizing Converters and Negotiation

5.1 Append Custom Converter

Implement WebMvcConfigurer, override extendMessageConverters to add on top of defaults:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new ProtobufHttpMessageConverter());
    }
}

5.2 Replace Entire Converter List

Override configureMessageConverters – replaces defaults; you must manually add all needed converters (Jackson, String, etc.). Prefer extend for most cases.

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
    converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
    // add others manually
}

5.3 Customize JSON Serialization

Define an ObjectMapper bean; Spring Boot injects it into Jackson converter. Also configurable via spring.jackson.* properties.

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    return mapper;
}

5.4 Customize Content Negotiation

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer
        .favorParameter(true)
        .parameterName("format")
        .defaultContentType(MediaType.APPLICATION_JSON)
        .mediaType("json", MediaType.APPLICATION_JSON)
        .mediaType("xml", MediaType.APPLICATION_XML);
}

5.5 Complete Custom Converter Example

Example for a custom YAML format:

public class YamlMessageConverter extends AbstractHttpMessageConverter<YamlData> {
    public YamlMessageConverter() {
        super(MediaType.parseMediaType("application/x-yaml"));
    }
    @Override
    protected boolean supports(Class<?> clazz) {
        return YamlData.class.isAssignableFrom(clazz);
    }
    @Override
    protected YamlData readInternal(Class<? extends YamlData> clazz, HttpInputMessage inputMessage) throws IOException {
        return YamlParser.parse(inputMessage.getBody(), clazz);
    }
    @Override
    protected void writeInternal(YamlData data, HttpOutputMessage outputMessage) throws IOException {
        outputMessage.getBody().write(YamlFormatter.format(data).getBytes(StandardCharsets.UTF_8));
    }
}

Register it; when negotiation yields application/x-yaml, this converter handles it.

Understanding HTTP message converters, content negotiation, and the Spring MVC request pipeline is essential for backend engineering and senior interviews. Mastering these mechanisms enables precise troubleshooting of encoding issues, 415/406 errors, and format negotiation failures, and informs REST API design, serialization customization, and multi-format endpoints.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

SerializationJSONSpring BootREST APIXMLSpring MVCHttpMessageConverterContent Negotiation
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.