How does ContentNegotiationManager work, what ContentNegotiationStrategy implementations exist, and how do you configure them?
answer
- Manager = ordered list of strategies, first non-*/* wins
- Header / Parameter / Fixed / (deprecated PathExtension)
- configureContentNegotiation(ContentNegotiationConfigurer)
- favorParameter + parameterName + mediaType map
- defaultContentType = FixedStrategy fallback
basics
~20 sContentNegotiationManager 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@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
Know that a manager drives the choice and the Accept header is the default signal.
Name Header vs Parameter strategies and the configurer's favorParameter/defaultContentType.
Explain strategy ordering, first-non-/ resolution, and how resolved types feed converter selection to 406.
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.