What is EndpointMediaTypes, and how do you control the media types (produces/consumes and content negotiation) of a custom web endpoint?
answer
- EndpointMediaTypes = global produce/consume list
- default = vnd.spring-boot.actuator.v3+json + application/json
- @ReadOperation(produces=...), @WriteOperation(consumes=...)
- Producible enum -> Accept-based negotiation
- 406 on Accept miss, 415 on Content-Type miss
basics
~10 sEndpointMediaTypes lists the media types web endpoints produce and consume (default: the actuator vendor JSON types plus application/json). Per operation you set @ReadOperation(produces=...) / @WriteOperation(consumes=...), and use a Producible enum for content negotiation.
solid answer
~40 sEndpointMediaTypes is a Spring Boot component holding the produced and consumed media types for web endpoints. By default it produces application/vnd.spring-boot.actuator.v3+json (and v2) plus application/json, and consumes application/vnd.spring-boot.actuator.v3+json and application/json — that's why Actuator responses carry the vendor media type. To narrow an individual operation you use the annotation attributes: @ReadOperation(produces = "text/plain"), @WriteOperation(consumes = "application/json"). For version/format negotiation from the Accept header, a read operation can return a type implementing the Producible interface (an enum), and Actuator picks the variant matching the client's Accept — this is how the actuator API version is selected. You can also return a WebEndpointResponse to set status and content. Note produces/consumes only apply to web endpoints; JMX has no media types. Overriding EndpointMediaTypes as a bean changes the global defaults for all endpoints.
code
java · 24 linesimport org.springframework.boot.actuate.endpoint.annotation.*;
import org.springframework.boot.actuate.endpoint.web.Producible;
import org.springframework.util.MimeType;
@Component
@Endpoint(id = "report")
public class ReportEndpoint {
// Emits plain text instead of the default vendor JSON
@ReadOperation(produces = "text/plain")
public String textReport() { return renderText(); }
}
// Content negotiation: enum implementing Producible drives Accept matching
enum ReportFormat implements Producible<ReportFormat> {
JSON("application/json"),
CSV("text/csv");
private final MimeType mimeType;
ReportFormat(String t) { this.mimeType = MimeType.valueOf(t); }
@Override public MimeType getProducedMimeType() { return mimeType; }
@Override public boolean isDefault() { return this == JSON; }
}go deeper
Know operations can set produces/consumes and default output is JSON.
Explain the default vendor media types and 406/415 behavior.
Use Producible for Accept-based negotiation and WebEndpointResponse for status.
Weigh overriding the global EndpointMediaTypes bean vs per-operation control, and design versioned media-type schemas.
## EndpointMediaTypes — the global default `org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes` is an auto-configured bean describing, for **web** endpoints, the list of media types they **produce** and **consume**. Its defaults are why Actuator responses are typed as a vendor media type rather than plain `application/json`: - **Produced**: `application/vnd.spring-boot.actuator.v3+json`, `application/vnd.spring-boot.actuator.v2+json`, `application/json` - **Consumed**: `application/vnd.spring-boot.actuator.v3+json`, `application/json` The `vnd.spring-boot.actuator.vN+json` types are **versioned vendor media types**: a client can send `Accept: application/vnd.spring-boot.actuator.v3+json` to pin a response schema version. You can replace the defaults by defining your own `EndpointMediaTypes` bean, which affects **all** web endpoints. ## Per-operation produces / consumes Each operation annotation carries media-type attributes to narrow that operation: ```java @ReadOperation(produces = "text/plain") public String dump() { ... } @WriteOperation(consumes = MediaType.APPLICATION_JSON_VALUE) public void update(String value) { ... } ``` - `produces` restricts what the read/write operation emits (drives the `Content-Type` and participates in `Accept` matching). A request whose `Accept` matches none of the produced types gets **406 Not Acceptable**. - `consumes` restricts the acceptable request `Content-Type` for a write operation; a mismatch yields **415 Unsupported Media Type**. This is how, e.g., the `prometheus` endpoint produces `text/plain` while most endpoints produce JSON. ## Content negotiation via Producible For an operation that can emit **several variants** selected by the client's `Accept` header, return a type implementing `org.springframework.boot.actuate.endpoint.web.Producible` — typically an **enum** whose constants map to media types (one marked default). Actuator inspects the request `Accept` header and chooses the matching enum constant, so a single operation serves multiple representations/versions without branching on headers manually. The built-in API versioning (`ApiVersion` implements `Producible`) works this way. ## WebEndpointResponse `WebEndpointResponse<T>` wraps a payload with an explicit HTTP status (and content), letting a web operation return, say, 410 or 503 alongside a body — complementing produces/consumes with status control. ## Scope and gotchas - Media types apply to **web** endpoints only; JMX has no notion of media types, so `produces`/`consumes` are ignored there. - Overriding the `EndpointMediaTypes` bean is global; prefer per-operation `produces`/`consumes` for a single endpoint. - A mismatched `Accept` → **406**; a mismatched request `Content-Type` on a write → **415**. - The default vendor media type surprises clients that hard-code `Content-Type: application/json` equality checks — `application/json` is included by default, but the vendor type is preferred in negotiation. - `Producible` must be an enum (or a type Actuator can enumerate) with exactly one default; getting the default wrong changes the no-`Accept` behavior.
- Why do Actuator responses show Content-Type application/vnd.spring-boot.actuator.v3+json instead of application/json?EndpointMediaTypes defaults produce the versioned vendor media type (v3/v2) ahead of application/json, so content negotiation prefers it. Clients can pin a schema version via the Accept header.
- How would you let one read operation return either JSON or CSV based on the client's Accept header?Return a type implementing Producible (an enum with getProducedMimeType and one isDefault=true). Actuator matches the Accept header to a constant and selects that representation.
saying these in an interview costs you the question
- Thinking produces/consumes affect JMX endpoints
- Assuming responses are plain application/json by default
- Confusing 406 (Accept miss) with 415 (Content-Type miss)
- Believing you must parse the Accept header manually instead of using Producible