Content Negotiation and HttpMessageConverters in Spring Boot 3

Introduction

A modern API often needs to return different response formats depending on client needs (JSON, XML, sometimes custom media types). In this chapter, you will learn how Spring Boot performs content negotiation, how HttpMessageConverter works, and how to extend converters safely when default behavior is not enough.

Prerequisites

  • Completed:
    • 06_spring_mvc_basics_annotations_and_routing.md
    • 08_file_upload_download_and_web_extensions.md
  • Basic understanding of request headers and controller return values

What Is Content Negotiation?

Content negotiation is the process where the server selects a response representation based on request context.

Typical signals:

  • Accept header (recommended standard)
  • URL parameter strategy (optional)
  • produces constraints on endpoint mapping

Example:

http
GET /api/users/1
Accept: application/json

vs

http
GET /api/users/1
Accept: application/xml

Same endpoint, different response formats.

Default Behavior in Spring Boot

With spring-boot-starter-web:

  • JSON is enabled by default (Jackson)
  • Spring picks a converter that supports:
    • method return type
    • selected media type

In most APIs, JSON is enough. But negotiation matters when you support multiple clients or backward-compatible integrations.

Force Output Type with produces

You can constrain endpoint output:

If client Accept does not match produces, Spring may return 406 Not Acceptable.

Add XML Response Support

By default, XML may not be enabled unless proper converter dependency is present.

Add dependency:

xml
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

DTO example:

Controller:

Test with:

bash
curl -H "Accept: application/xml" http://localhost:8080/api/users/1

Configure Parameter-Based Negotiation (Optional)

Header-based negotiation is standard, but Spring also supports query-parameter based negotiation.

application.yml example:

yaml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: format
      media-types:
        json: application/json
        xml: application/xml

Then:

  • /api/users/1?format=json
  • /api/users/1?format=xml

Warning

Parameter-based negotiation is convenient for demos, but Accept header is more REST-standard and usually preferred for production APIs.

How HttpMessageConverter Works

HttpMessageConverter converts:

  • request body -> Java object
  • Java object -> response body

Examples of built-in converters:

  • MappingJackson2HttpMessageConverter (JSON)
  • StringHttpMessageConverter (text/plain)
  • ByteArrayHttpMessageConverter (binary)
  • XML converter (if XML dependency is present)

Selection depends on:

  1. Java type
  2. media type
  3. converter capability (canRead / canWrite)

Register Custom Converter

Suppose you want to support YAML output.

Add dependency:

xml
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

Custom converter:

Register converter:

Now clients can request:

http
Accept: text/yaml

Common Negotiation Errors

406 Not Acceptable

Reason:

  • Requested media type unsupported
  • Endpoint produces does not match Accept

Fix:

  • add converter/dependency
  • adjust produces
  • send supported Accept

415 Unsupported Media Type

Reason:

  • Request body media type unsupported
  • Missing Content-Type for request body

Fix:

  • send Content-Type: application/json (or correct type)
  • ensure converter can read that type

Unexpected converter chosen

Reason:

  • converter order issues
  • broad media type support in custom converter

Fix:

  • narrow custom media types
  • review converter registration order

Best Practices

  • Prefer JSON as primary public API format unless integration requires others
  • Use Accept header as default negotiation mechanism
  • Keep custom converters focused and explicit in supported media types
  • Avoid overloading one endpoint with too many formats unless truly needed
  • Add integration tests per media type to prevent regression

Quick Recap

In this chapter, you learned:

  1. How Spring Boot decides response format
  2. How to use produces and Accept
  3. How to add XML/YAML capabilities
  4. How HttpMessageConverter participates in request/response conversion
  5. How to troubleshoot 406/415 media-type errors

Next chapter moves into data-access foundations, starting with JDBC data source configuration and connection pool basics.

FAQ

Should every API support XML?

No. JSON-only APIs are common. Add XML only when required by client contracts or legacy integrations.

Is query-parameter negotiation a bad practice?

Not always, but header-based negotiation is more standard and clearer for HTTP semantics.

Can custom converter break default JSON behavior?

Yes, if configured too broadly. Keep custom converter media types specific and test default JSON routes.

What is the difference between consumes and produces?

  • consumes: what request body content type endpoint accepts
  • produces: what response content type endpoint emits

Do I need custom converters for most projects?

Usually no. Default converters cover common JSON/text/binary needs very well.