skip to content

How does ContentNegotiationManager work, what ContentNegotiationStrategy implementations exist, and how do you configure them?

level: seniorimportance: should knowfreq 52%

answer

  1. Manager = ordered list of strategies, first non-*/* wins
  2. Header / Parameter / Fixed / (deprecated PathExtension)
  3. configureContentNegotiation(ContentNegotiationConfigurer)
  4. favorParameter + parameterName + mediaType map
  5. defaultContentType = FixedStrategy fallback

basics

~20 s

ContentNegotiationManager holds an ordered list of ContentNegotiationStrategy objects and calls each to resolve requested media types. Built-in strategies read the Accept header, a query parameter (?format=json), or a fixed default. You configure it via WebMvcConfigurer.configureContentNegotiation.

solid answer

~30 s

`ContentNegotiationManager` is the central component that turns a request into a prioritized list of requested media types. It is itself a `ContentNegotiationStrategy` that delegates to an ordered list of strategies, returning the first non-empty result. Built-in strategies: `HeaderContentNegotiationStrategy` (parses the `Accept` header, honoring q-values), `ParameterContentNegotiationStrategy` (maps a query param like `?format=json` to a media type), `FixedContentNegotiationStrategy` (a hard default), and the deprecated path-extension strategies. You configure it by overriding `configureContentNegotiation(ContentNegotiationConfigurer)` in a `WebMvcConfigurer` — e.g. `favorParameter(true).parameterName("format").mediaType("json", APPLICATION_JSON).defaultContentType(APPLICATION_JSON).ignoreAcceptHeader(false)`. The manager's resolved types are then matched by `RequestResponseBodyMethodProcessor` against producible types and converters; no match → 406.

code

java · 30 lines
java
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
            // Enable ?format=json / ?format=xml (ParameterContentNegotiationStrategy)
            .favorParameter(true)
            .parameterName("format")
            // key -> media type table used by the parameter strategy
            .mediaType("json", MediaType.APPLICATION_JSON)
            .mediaType("xml", MediaType.APPLICATION_XML)
            // Still honor the Accept header (HeaderContentNegotiationStrategy)
            .ignoreAcceptHeader(false)
            // Fallback when nothing else resolves (FixedContentNegotiationStrategy)
            .defaultContentType(MediaType.APPLICATION_JSON);
    }
}

// A custom strategy, e.g. read a versioned vendor media type from a header.
class ApiVersionStrategy implements ContentNegotiationStrategy {
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest request) {
        String v = request.getHeader("X-Api-Version");
        if ("2".equals(v)) {
            return List.of(MediaType.parseMediaType("application/vnd.myapi.v2+json"));
        }
        return MEDIA_TYPE_ALL_LIST; // defer to the next strategy
    }
}

go deeper

for a junior

Know that a manager drives the choice and the Accept header is the default signal.

for a middle

Name Header vs Parameter strategies and the configurer's favorParameter/defaultContentType.

for a senior

Explain strategy ordering, first-non-/ resolution, and how resolved types feed converter selection to 406.

for a principal

Design custom/versioned negotiation strategies and reason about security (path-extension) and API-contract implications of each signal.

**`ContentNegotiationStrategy`** is a simple interface: `List<MediaType> resolveMediaTypes(NativeWebRequest request)`. Each implementation answers 'what media types is this request asking for?' from a different signal. **`ContentNegotiationManager`** implements both `ContentNegotiationStrategy` and `MediaTypeFileExtensionResolver`. Internally it holds an **ordered list** of strategies and, on `resolveMediaTypes`, calls them in order, returning the **first** result that isn't the sentinel `MEDIA_TYPE_ALL_LIST` (`[*/*]`). So earlier strategies win; a strategy that finds nothing specific yields `*/*` and the next strategy gets a chance. **Built-in strategies (the important ones):** - **`HeaderContentNegotiationStrategy`** — parses the `Accept` request header into a list of `MediaType`s sorted by specificity and **q-value** (quality weight). This is the default and the RFC-standard mechanism. - **`ParameterContentNegotiationStrategy`** — reads a request parameter (default name `format`) and maps its value (e.g. `json`) to a `MediaType` via a configured key→media-type table. Enabled by `favorParameter(true)`. Useful for browsers/links where you can't set headers. - **`FixedContentNegotiationStrategy`** — always returns a fixed list; used to express `defaultContentType(...)` — the fallback when other strategies yield `*/*`. - **`PathExtensionContentNegotiationStrategy` / `ServletPathExtensionContentNegotiationStrategy`** — mapped a URL suffix like `/data.json` to a media type. **Deprecated and disabled by default** since Spring 5.3 (`favorPathExtension` defaults to `false`) because suffix matching enables Reflected File Download (RFD) and content-sniffing security issues and complicates routing. **Configuration surface — `ContentNegotiationConfigurer`** (via `WebMvcConfigurer.configureContentNegotiation`): - `favorParameter(boolean)` + `parameterName(String)` — enable/name the query-param strategy. - `mediaType("json", MediaType.APPLICATION_JSON)` — register key↔media-type mappings (used by the parameter strategy). - `defaultContentType(MediaType...)` — the fixed fallback when nothing else resolves; adds a `FixedContentNegotiationStrategy` at the end. - `ignoreAcceptHeader(boolean)` — drop the header strategy entirely (useful when you *only* want param-based negotiation). - `strategies(List<ContentNegotiationStrategy>)` — replace the whole strategy list with your own. - `defaultContentTypeStrategy(ContentNegotiationStrategy)` — custom fallback logic (e.g. per-user default). - `favorPathExtension(...)` — deprecated; avoid re-enabling. **Spring Boot note:** Boot exposes `spring.mvc.contentnegotiation.*` properties (`favor-parameter`, `parameter-name`, `media-types.*`) and builds the manager for you; you can still add a `WebMvcConfigurer` to customize. **How the resolved types are used:** After `ContentNegotiationManager.resolveMediaTypes` returns the acceptable list, `AbstractMessageConverterMethodProcessor.writeWithMessageConverters` (parent of `RequestResponseBodyMethodProcessor`) computes producible types from converters (∩ `produces`), then picks the best acceptable∩producible pair (most specific, honoring q-value order). If the intersection is empty → `HttpMediaTypeNotAcceptableException` → **406**. **Custom strategy use cases:** version-based negotiation (`Accept: application/vnd.myapi.v2+json`), tenant/user default formats, or reading a custom header. Implement `ContentNegotiationStrategy` and register via `strategies(...)` or `defaultContentTypeStrategy(...)`. **Gotchas:** - If you enable `favorParameter` but forget the `mediaType` mappings, `?format=json` resolves to nothing useful. - Order matters: parameter strategy typically registered *before* header so `?format=` overrides `Accept`. - `ignoreAcceptHeader(true)` with no default and no param → everything becomes `*/*` and the first producible wins, which can surprise clients.

  • You enabled favorParameter but ?format=json still doesn't switch the response. What's the likely cause?
    Missing `mediaType("json", MediaType.APPLICATION_JSON)` mapping, or the header strategy runs first and the client's Accept already resolves to something. Also confirm the endpoint's converters/produces actually support JSON.
  • Why is path-extension content negotiation (/api/data.json) disabled by default now?
    Security and ambiguity: suffix matching enabled Reflected File Download (RFD) attacks and content-type sniffing issues, and it complicated URL routing. Since Spring 5.3 `favorPathExtension` defaults to false and the strategies are deprecated.

saying these in an interview costs you the question

  • Claiming the manager tries all strategies and merges results — it returns the first non-`*/*` result in order.
  • Saying path-extension negotiation is still the recommended/default approach.
  • Confusing the `mediaType(key, type)` map with produces — the map is only for the parameter/extension key lookup.

context