skip to content

Message Conversion & Content Negotiation

How objects become response bytes and request bytes become objects: HttpMessageConverters, content negotiation, Jackson configuration and ResponseEntity. Most API-shaping questions in a Spring interview live here.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

20

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

open as a page

What is an HttpMessageConverter in Spring MVC, and when does Spring use one?

level: juniorimportance: must knowfreq 70%

basics

~10 s

It converts between HTTP request/response bodies (bytes) and Java objects. Spring uses it whenever you annotate a parameter with @RequestBody or a return value with @ResponseBody (including @RestController).

open as a page

How does Spring Boot set up Jackson's ObjectMapper for a REST controller, and how do you tweak its behavior without writing Java?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Spring Boot auto-configures a Jackson ObjectMapper bean and uses it in MappingJackson2HttpMessageConverter to turn objects into JSON. You tweak it declaratively with spring.jackson.* properties in application.yml/properties.

open as a page

What is ResponseEntity<T> in Spring MVC, and why would you return it from a controller instead of returning the body object directly?

level: juniorimportance: must knowfreq 80%

basics

~10 s

ResponseEntity<T> represents the full HTTP response: status code, headers, and body. You return it (instead of a plain object) when you need to control the status code or add headers, not just the body.

open as a page

Explain produces vs consumes on a request mapping, and which HTTP status each drives when it fails to match.

level: middleimportance: must knowfreq 70%

basics

~10 s

consumes matches the request body's Content-Type; a mismatch yields 415 Unsupported Media Type. produces matches the client's Accept header (what the response can be); a mismatch yields 406 Not Acceptable.

open as a page

How does Spring Boot serialize java.time types like LocalDateTime, and how do you control the exact format?

level: middleimportance: must knowfreq 68%

basics

~10 s

The JavaTimeModule (jackson-datatype-jsr310) handles java.time types, and Spring Boot disables WRITE_DATES_AS_TIMESTAMPS so they serialize as ISO-8601 strings. For a specific field, override with @JsonFormat(pattern = ...).

open as a page

Walk through the ResponseEntity builder/factory API: what do ok(), created(), notFound(), and noContent() do, and how do you attach custom headers?

level: middleimportance: must knowfreq 72%

basics

~10 s

ok() = 200, created(uri) = 201 with a Location header, notFound() = 404, noContent() = 204. Each returns a builder; you add headers via .header(name, value) or .headers(HttpHeaders), then finish with .body(x) or .build().

open as a page

How does Spring select which HttpMessageConverter to use for a given request or response? Explain canRead/canWrite, ordering, and content negotiation.

level: seniorimportance: must knowfreq 60%

basics

~20 s

Spring iterates the ordered converter list and picks the first whose canRead/canWrite returns true for both the Java type and the media type. For writing, content negotiation (the Accept header) determines the target media type; for reading it's the request's Content-Type.

open as a page

What's the correct way to customize the ObjectMapper in Spring Boot, and what's the pitfall of defining your own ObjectMapper bean?

level: seniorimportance: must knowfreq 60%

basics

~10 s

Prefer a Jackson2ObjectMapperBuilderCustomizer bean or spring.jackson.* properties, which tweak the auto-configured mapper while keeping Boot's defaults. Defining your own @Bean ObjectMapper makes auto-config back off, so you lose those defaults.

open as a page

Name the main built-in HttpMessageConverters and what Java type / media type each handles.

level: middleimportance: should knowfreq 55%

basics

~10 s

ByteArrayHttpMessageConverter (byte[]), StringHttpMessageConverter (String / text/plain), MappingJackson2HttpMessageConverter (JSON), and FormHttpMessageConverter (form-urlencoded / multipart into a MultiValueMap). Jackson is only registered if it's on the classpath.

open as a page

Compare @ResponseStatus on a (void) handler with returning a ResponseEntity. When would you choose each, and how do they interact if both are present?

level: middleimportance: should knowfreq 60%

basics

~20 s

@ResponseStatus declares a fixed status for a handler (or exception) at compile time — good when the status never varies, often with void handlers returning no body. ResponseEntity sets status per-response at runtime. If both apply, the ResponseEntity's status wins.

open as a page

Walk through how Spring resolves the exact response type when the Accept header lists several media types with q-values and the server has multiple converters.

level: seniorimportance: should knowfreq 40%

basics

~20 s

Spring parses Accept into media types sorted by q-value and specificity, computes the types the converters (and any produces) can write, then intersects the two lists and picks the most specific, highest-priority match. If the intersection is empty it returns 406.

open as a page

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

level: seniorimportance: should knowfreq 52%

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.

open as a page

How do you add, remove, reorder, or reconfigure HttpMessageConverters? Contrast configureMessageConverters with extendMessageConverters.

level: seniorimportance: should knowfreq 45%

basics

~20 s

Implement WebMvcConfigurer. Override extendMessageConverters(list) to tweak the existing defaults (add, remove, reorder) while keeping them. Override configureMessageConverters(list) only if you want to fully REPLACE the defaults — if that list is non-empty, Spring adds no defaults.

open as a page

What is @JsonView and how do you use it in Spring MVC to expose different fields on different endpoints?

level: seniorimportance: should knowfreq 45%

basics

~10 s

@JsonView lets one class expose different field subsets per endpoint. You define marker (view) classes, tag fields with @JsonView(View.class), and put @JsonView on the controller method to pick which view serializes.

open as a page

For a POST that creates a resource, how do you correctly return 201 with a Location header using ResponseEntity, and how should you build that URI?

level: seniorimportance: should knowfreq 55%

basics

~10 s

Use ResponseEntity.created(uri).body(dto) — created() sets 201 and the Location header. Build the URI from the current request, e.g. ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri(), so it reflects the real base URL.

open as a page

Which Jackson serialization/mapper features matter for building a stable, resilient JSON API, and how do you configure them globally vs per field?

level: principalimportance: should knowfreq 40%

basics

~10 s

Set FAIL_ON_UNKNOWN_PROPERTIES=false for forward-compatible input, choose a global default-property-inclusion (e.g. non_null), disable FAIL_ON_EMPTY_BEANS, register JavaTimeModule, and use annotations like @JsonInclude/@JsonFormat/@JsonProperty for per-field overrides.

open as a page

Why did Spring deprecate and disable path-extension (suffix) content negotiation by default, and how should APIs handle format selection now?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

URL suffixes like /data.json for negotiation caused security problems (Reflected File Download, content sniffing) and routing ambiguity. Since Spring 5.3 path-extension negotiation is off by default. Prefer the Accept header, or an explicit query parameter, for format selection.

open as a page

As a principal engineer, discuss non-obvious HttpMessageConverter behaviors: the StringHttpMessageConverter charset default, converter reuse across RestTemplate, and how the write-side producible-type computation can surprise you.

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Key gotchas: StringHttpMessageConverter defaults to ISO-8859-1 when no charset is given (garbling UTF-8 text); the same converter SPI is reused by RestTemplate on the client side; and because String and byte[] converters advertise /, they can quietly pre-empt Jackson depending on ordering and Accept.

open as a page

As a tech lead, how do you decide between returning DTOs, ResponseEntity, @ResponseStatus, and centralized shaping (ResponseBodyAdvice/@ControllerAdvice) to keep response handling consistent across a large codebase?

level: principalimportance: nice to knowfreq 32%

basics

~20 s

Default to plain DTOs for simple 200s; use @ResponseStatus for fixed non-200 statuses; use ResponseEntity when status or headers vary per request. Centralize cross-cutting shaping (envelopes, error format) in @ControllerAdvice / ResponseBodyAdvice / an ExceptionHandler, not per-controller.

open as a page