skip to content

What is content negotiation in Spring MVC, and how does Spring decide what format (JSON, XML) to send back?

level: juniorimportance: must knowfreq 62%

answer

  1. Accept header in, Content-Type out
  2. ContentNegotiationManager resolves media types
  3. HttpMessageConverters produce them
  4. 406 when nothing acceptable
  5. produces narrows what a method emits

basics

~20 s

Content negotiation is how Spring picks the response format. The client sends an Accept header (e.g. application/json); Spring matches it against the formats a handler can produce and the available message converters, then writes that format.

solid answer

~40 s

Content negotiation is the process of selecting the response media type based on what the client wants and what the server can produce. The client expresses its preference primarily through the HTTP Accept header (e.g. `Accept: application/json`). For a `@ResponseBody`/`@RestController` method, Spring's ContentNegotiationManager resolves the requested media types, then RequestResponseBodyMethodProcessor walks the registered HttpMessageConverters to find one that can serialize the return object into an acceptable type. If a converter matches (e.g. Jackson for JSON), it writes that format and sets Content-Type. A handler can also declare `produces = "application/json"` on `@RequestMapping`/`@GetMapping` to restrict which types it emits. If nothing the server can produce satisfies the Accept header, Spring returns 406 Not Acceptable.

code

java · 11 lines
java
@RestController
public class ProductController {

    // Returns JSON when the client sends Accept: application/json.
    // 'produces' restricts this method to JSON; a request asking only
    // for application/xml would get 406 Not Acceptable.
    @GetMapping(value = "/products/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Product getProduct(@PathVariable long id) {
        return new Product(id, "Widget");
    }
}

go deeper

for a junior

Know that the Accept header requests a format and Spring answers with a matching Content-Type; JSON is the common default.

for a middle

Explain the manager + converter interplay and the role of the produces attribute.

for a senior

Discuss the resolveMediaTypes flow, q-values, and the 406/415 distinction precisely.

for a principal

Reason about default fallbacks, multi-format APIs, and when to override negotiation defaults for API contract stability.

**Content negotiation** is the mechanism by which an HTTP server and client agree on the *representation* (format) of a resource — most commonly JSON vs XML. In Spring MVC it answers the question: 'The controller method returns a `Product` object; should I serialize it as JSON, XML, or something else, and how do I choose?' **The two inputs to the decision:** 1. **What the client wants** — expressed via the request's `Accept` header, e.g. `Accept: application/json` or `Accept: application/xml, application/json;q=0.9`. The `q` value (0–1) is a *quality* weight expressing relative preference. 2. **What the server can produce** — the set of registered `HttpMessageConverter`s (a `MappingJackson2HttpMessageConverter` for JSON, `Jaxb2RootElementHttpMessageConverter`/`MappingJackson2XmlHttpMessageConverter` for XML, `StringHttpMessageConverter`, etc.), optionally narrowed by a `produces` attribute on the mapping. **The flow for a body-returning method:** - The handler method returns an object (via `@ResponseBody` or being inside a `@RestController`). - `RequestResponseBodyMethodProcessor` (a `HandlerMethodReturnValueHandler`) is invoked. - It asks the **`ContentNegotiationManager`** to `resolveMediaTypes(request)` — this returns the list of acceptable media types, sorted by preference. - It computes the *producible* types: for each converter that `canWrite` the return type, its supported media types (intersected with `produces` if declared). - It finds the best match between acceptable and producible types. - The winning converter serializes the object and sets the `Content-Type` response header. **Key annotations:** - `produces` — on `@GetMapping(produces = "application/json")`: declares the media type(s) this method emits; participates in negotiation and also acts as a mapping condition. - `consumes` — the mirror for *request* bodies: it matches the incoming `Content-Type`. **Failure outcomes:** - **406 Not Acceptable** (`HttpMediaTypeNotAcceptableException`): the server cannot produce anything the client's `Accept` header allows. - **415 Unsupported Media Type** (`HttpMediaTypeNotSupportedException`): the client sent a request body whose `Content-Type` no `consumes`/converter accepts. **When to care:** Almost every REST API relies on this implicitly — Jackson-for-JSON 'just works' because Spring Boot auto-registers the converter and the default Accept negotiation. You reach for explicit configuration when you support multiple formats, want a `?format=json` query parameter, or need a sensible default when the client sends no Accept header.

  • If the client sends no Accept header at all, what happens?
    By default it is treated as `Accept: */*`, so Spring picks the first producible type (typically JSON if Jackson is on the classpath). You can override the fallback with `ContentNegotiationConfigurer.defaultContentType(...)`.
  • Where do the JSON/XML converters come from in Spring Boot?
    Boot auto-registers `HttpMessageConverter`s based on the classpath — `MappingJackson2HttpMessageConverter` when Jackson is present, XML converters when Jackson-XML or JAXB is present — via `HttpMessageConvertersAutoConfiguration`.

saying these in an interview costs you the question

  • Thinking the Content-Type of the *request* decides the *response* format (that's Accept's job; Content-Type describes the request body).
  • Believing the file extension in the URL (.json) still drives negotiation by default — it does not in modern Spring.
  • Confusing 406 (can't produce what client wants) with 415 (can't read what client sent).

context