Foundation · Build

Content Negotiation

The Accept header picks among the formats your HttpMessageConverters support; negotiation fails loudly when no converter can produce a match.

Primary source: Spring MVC: Content Negotiation

15 min

Lesson 6 of 11

1d streak

The Architectural Question

APIs hard-coded to JSON cannot serve partners locked into XML without duplicating every endpoint or bolting on an external transformer.

What this guide proves

  • Accept Header
  • HttpMessageConverter
  • Jackson/JAXB

Content-Type vs Accept: Two sides of the media negotiation

HTTP content negotiation allows clients and servers to agree on the data format used in request payloads and response bodies. A single endpoint can serve both JSON and XML without altering business logic.

Architectural Boundary · Do Not Confuse

Concept A

Content-Type Header

Concept B

Accept Header

Content-Type specifies the format of the data being sent in the current request body (e.g., application/json). Accept specifies the media types that the client is willing to receive in the response body (e.g., application/json, application/xml).

How Spring resolves HttpMessageConverters

  1. 01Client sends GET /api/orders with Accept: application/xml.
  2. 02DispatcherServlet consults ContentNegotiationManager to parse requested MediaTypes in priority order.
  3. 03HandlerAdapter executes the controller method and receives an OrderDto object.
  4. 04RequestResponseBodyMethodProcessor iterates through all registered HttpMessageConverter beans.
  5. 05If jackson-dataformat-xml is present, MappingJackson2XmlHttpMessageConverter matches both the MediaType and the return class.
  6. 06If no converter can satisfy the Accept header, Spring aborts with HttpMediaTypeNotAcceptableException (406 Not Acceptable).

Dual JSON and XML representation in practice

pom.xml
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
CustomerController.java
package com.example.customer.api;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/customers")
public class CustomerController {

    @GetMapping(value = "/{id}", produces = {"application/json", "application/xml"})
    public CustomerResponse getCustomer(@PathVariable Long id) {
        return new CustomerResponse(id, "Acme Corp", "contact@acme.com");
    }
}

record CustomerResponse(Long id, String name, String email) {}

Break it deliberately: 406 Not Acceptable vs 415 Unsupported Media Type

Terminal / Diagnostic Output
Test 1: Request with unsupported Accept header
curl -i -H "Accept: application/yaml" http://localhost:8080/api/customers/1
HTTP/1.1 406 Not Acceptable

Test 2: POST request with unsupported Content-Type header
curl -i -X POST -H "Content-Type: text/plain" -d "raw text" http://localhost:8080/api/customers
HTTP/1.1 415 Unsupported Media Type

Active Recall Checkpoint

What is the diagnostic difference between an HTTP 406 error and an HTTP 415 error in Spring MVC?

Hint: Think about which header failed: incoming request body vs outgoing response body.

Show answer & explanation

415 Unsupported Media Type occurs when the server does not have a converter capable of reading the request body specified by the Content-Type header. 406 Not Acceptable occurs when the server cannot serialize the response into any of the media types requested in the client’s Accept header.

Negotiation strategies in production

Use it when

Rely on standard Accept headers for content negotiation when building public enterprise APIs serving heterogeneous clients.

Reconsider when

Avoid using URL file extensions (e.g. /customers/1.json) as they complicate route mapping and are deprecated in modern Spring Framework versions.

Runtime Architecture & Flow

Decision canvas

Evaluate the choice across three constraints

These are comparison criteria, not sequential steps. A strong answer states which constraint dominates in the current system.

CRITERION 01

Accept Header

Ask how each option changes this guarantee, its operational cost, and its failure mode.

CRITERION 02

HttpMessageConverter

Ask how each option changes this guarantee, its operational cost, and its failure mode.

CRITERION 03

Jackson/JAXB

Ask how each option changes this guarantee, its operational cost, and its failure mode.

Decision prompt: Add the Jackson XML dependency and use Postman to request the same endpoint with `Accept: application/json` and `Accept: application/xml`.

Working Implementation

Code Walkthrough

Add the Jackson XML dependency and use Postman to request the same endpoint with `Accept: application/json` and `Accept: application/xml`.

ContentNegotiationLab.java
package com.example.content;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/reports")
public class ContentNegotiationLab {

    public record ReportSummary(String reportId, String title, int count) {}

    // Produces both JSON and XML depending on the incoming 'Accept' header
    @GetMapping(
        value = "/{id}",
        produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }
    )
    public ReportSummary getReport(@PathVariable String id) {
        return new ReportSummary(id, "Monthly Performance", 42);
    }
}
Spring MVC inspects the HTTP Accept header to select the appropriate HttpMessageConverter.
MediaType.APPLICATION_JSON_VALUE uses MappingJackson2HttpMessageConverter; XML requires jackson-dataformat-xml.
If the client requests an unsupported media type (e.g. Accept: application/pdf), Spring returns HTTP 406 Not Acceptable.

Synthesis & Production Verification

Production Acceptance Criteria

  • Serve JSON and XML from one endpoint driven by the Accept header
  • Explain how converter selection order affects output
  • Predict 406 versus 415 for mismatched Accept and Content-Type values

Architectural & Technical Review

Core Interview Questions

Q01What is the functional difference between Content-Type and Accept headers?Show answer

Content-Type declares the format of the incoming request body. Accept specifies the media types acceptable for the outgoing response body.

Q02How does Spring select an HttpMessageConverter for response serialization?Show answer

It evaluates the requested Accept media types against registered converters, matching both media type and the controller method return type.

Q03What causes an HTTP 406 Not Acceptable status code?Show answer

When no registered HttpMessageConverter can produce any of the media types requested in the client Accept header.

Production Pitfalls

Critical Anti-Patterns

  • Pitfall: Using URL file extensions for content negotiation — Path extensions (e.g. /orders.json) complicate route patterns and are deprecated in modern Spring Framework versions.
  • Pitfall: Assuming all clients parse JSON by default — Enterprise clients may send explicit Accept headers or request XML; configure sensible media defaults or return 406 cleanly.

Primary Documentation & Specifications

Lesson Primary References

Supplementary Module Resources