What is ContentNegotiatingViewResolver and how does it decide which view to render? Explain content negotiation via Accept header vs path extension vs request parameter.
answer
- orchestrator, not a real resolver
- ContentNegotiationManager: Accept / extension / param
- gathers candidate Views, picks by content type
- must be ordered FIRST (highest precedence)
- path-extension disabled by default now
basics
~20 sIt's a ViewResolver that picks a view based on the requested media type (content negotiation). It determines the desired MIME type (from Accept header, URL extension, or a request param), then delegates to other ViewResolvers/Views and returns the one matching that media type.
solid answer
~40 sContentNegotiatingViewResolver doesn't resolve views itself — it orchestrates. For a request it first computes the list of requested media types using a ContentNegotiationManager (strategies: Accept header, path extension like .json/.xml, or a query parameter such as ?format=json). It then asks all the other configured ViewResolvers to resolve the logical view name, collecting candidate Views, plus any explicitly registered default views. Finally it selects the candidate View whose getContentType() best matches the requested media type, honoring the media-type priority order. This lets one controller endpoint serve HTML to a browser and JSON/XML to an API client from the same logical view name. It must be ordered with high priority so it runs before the delegate resolvers it wraps. Note: modern APIs usually prefer @ResponseBody + message converters over view-based negotiation.
code
java · 20 lines@Bean
public ContentNegotiatingViewResolver cnvr(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver r = new ContentNegotiatingViewResolver();
r.setContentNegotiationManager(manager);
// Fallback representations, chosen by their content type:
r.setDefaultViews(List.of(new MappingJackson2JsonView()));
// Delegate resolvers (e.g. ThymeleafViewResolver) are discovered from context.
return r;
}
// Enable a ?format=json param strategy (Accept header works out of the box):
@Configuration
class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer c) {
c.favorParameter(true).parameterName("format")
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("html", MediaType.TEXT_HTML);
}
}go deeper
Likely unaware of CNVR; not expected.
Can state that it picks a view by requested media type.
Must explain delegation, ContentNegotiationManager strategies, candidate selection, and ordering.
Discusses security-driven deprecation of extension negotiation and when view-based vs converter-based negotiation is the right architecture.
## What content negotiation means **Content negotiation** = the same URL returning different representations (HTML, JSON, XML, PDF) depending on what the client asks for. `ContentNegotiatingViewResolver` (CNVR) applies this at the **view layer**. ## It's an orchestrator, not a resolver CNVR implements `ViewResolver` but delegates all actual name→View work to the **other** `ViewResolver` beans in the context. Its job is to: 1. **Determine the requested media types** for the current request. 2. **Gather candidate Views** from every delegate resolver (and any `defaultViews`). 3. **Pick the best-matching View** by comparing each candidate's `getContentType()` against the requested media types. ## Step 1 — determining media types via ContentNegotiationManager CNVR uses a **`ContentNegotiationManager`** configured with one or more strategies (checked in order): - **`HeaderContentNegotiationStrategy`** — reads the HTTP **`Accept`** header (e.g. `Accept: application/json`). This is the standards-correct approach. - **`PathExtensionContentNegotiationStrategy`** — infers type from the URL **suffix**: `/report.json` → `application/json`, `/report.xml` → `application/xml`. (Disabled by default in recent Spring for security reasons — suffix pattern matching was turned off.) - **`ParameterContentNegotiationStrategy`** — reads a **request parameter**, e.g. `?format=json`. Off by default; you enable and name it. - **`FixedContentNegotiationStrategy`** — a hard default media type when nothing else matches. You tune these in Boot via `spring.mvc.contentnegotiation.*` or a `WebMvcConfigurer.configureContentNegotiation(...)`. ## Step 2 — gathering candidates CNVR calls each delegate resolver with the logical name, collecting every non-null `View`. You can also register **`defaultViews`** (e.g. a `MappingJackson2JsonView` for JSON, a `MarshallingView` for XML) so a JSON/XML representation exists even if no template resolver produces one. ## Step 3 — selecting the best match CNVR iterates the requested media types in priority order and returns the first candidate `View` whose content type is compatible. If nothing matches it returns null (falling through), or a 406 Not Acceptable can result depending on config. ## Ordering CNVR **must have higher precedence** (lower `order` value) than the resolvers it delegates to, so it runs first and controls selection. Spring Boot auto-configures it when appropriate and sets ordering. ## Concrete example Endpoint returns view name `"report"` and a model with a `report` attribute: - Browser (`Accept: text/html`) → Thymeleaf resolves `report.html`. - API client (`Accept: application/json`) → a registered `MappingJackson2JsonView` serializes the model to JSON. Same handler, two representations. ## Gotchas - **Path-extension strategy is disabled by default** in current Spring MVC (suffix pattern matching / `useSuffixPatternMatch` removed) due to RFD/security concerns — don't rely on `.json` in the URL unless you re-enable it. - **Ordering mistakes**: if CNVR isn't first, a catch-all resolver (JSP) shadows it. - **Accept: */\*** from some clients matches the first/default view — order media types deliberately. - **Modern alternative**: for pure APIs, `@RestController` / `@ResponseBody` with `HttpMessageConverter`s and `produces`/`Accept` negotiation is the mainstream path; CNVR shines when you genuinely mix rendered templates AND serialized data from one endpoint. ## When to use - Serving both a human HTML page and a machine JSON/XML feed from the same view-based controller. Otherwise prefer message-converter-based negotiation.
- Why is path-extension content negotiation (e.g. /data.json) discouraged/disabled by default in modern Spring?Suffix pattern matching and extension-based negotiation were disabled because they enabled Reflected File Download (RFD) and other security/ambiguity issues, and complicated URL mapping. Spring now favors the Accept header (and optionally an explicit request parameter) instead.
- When would you choose CNVR over @RestController with message converters?When one endpoint must serve BOTH a fully rendered HTML template (via Thymeleaf/JSP) and a serialized JSON/XML representation of the same model. For pure JSON/XML APIs with no server-rendered HTML, message converters (@ResponseBody/produces) are simpler and idiomatic.
saying these in an interview costs you the question
- Saying CNVR resolves templates itself instead of delegating
- Believing path-extension (.json) negotiation is on by default
- Ordering CNVR after its delegate resolvers
- Confusing view-layer negotiation with @ResponseBody message conversion