What do the consumes and produces attributes do on @RequestMapping / the mapping shortcuts, and how do they interact with request headers?
answer
- consumes -> Content-Type -> 415
- produces -> Accept -> 406 + sets response type
- Wildcards + negation: application/*, !text/plain
- Two handlers same path differ by produces
- Method-level overrides class-level, not merges
basics
~20 sconsumes restricts a handler to requests whose Content-Type matches (what the endpoint accepts in the body). produces restricts by the client's Accept header and sets the response content type. Non-matching requests get 415 (Unsupported Media Type) or 406 (Not Acceptable).
solid answer
~40 sconsumes and produces are media-type predicates. consumes filters on the request's Content-Type header — the handler only matches if the incoming body's type is in the list (e.g. consumes = "application/json"); a mismatch yields HTTP 415 Unsupported Media Type. produces filters on the request's Accept header — the handler matches only if the client is willing to accept one of the listed types, and it also sets the negotiated response Content-Type; a mismatch yields HTTP 406 Not Acceptable. Both accept multiple values and support wildcards like "application/*" and negation ("!text/plain"). They participate in content negotiation and let you have two handlers on the same path/verb that differ only by media type (e.g. one produces JSON, another XML). MediaType constants such as MediaType.APPLICATION_JSON_VALUE are the idiomatic way to specify them.
code
java · 16 lines@RestController
@RequestMapping("/reports")
public class ReportController {
// Only handles JSON bodies; other Content-Types -> 415
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public Report create(@RequestBody ReportDto dto) { ... }
// Accept: application/json -> this handler, sets JSON response
@GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public Report asJson(@PathVariable Long id) { ... }
// Accept: application/xml -> this handler instead (same path+verb)
@GetMapping(path = "/{id}", produces = MediaType.APPLICATION_XML_VALUE)
public Report asXml(@PathVariable Long id) { ... }
}go deeper
Knows consumes = request body type, produces = response type at a basic level.
Must map consumes->415 and produces->406 and know Accept vs Content-Type.
Explains media-type routing (two handlers, same path) and wildcard/negation syntax.
Distinguishes mapping-level predicates from global ContentNegotiationConfigurer strategy and its security/caching implications.
**Content negotiation background.** HTTP lets a client declare what it sends and what it wants back. The `Content-Type` request header describes the *body being sent*; the `Accept` request header lists media types the client is *willing to receive*. Spring MVC can route on both. **`consumes` — matches Content-Type.** It narrows a mapping to requests whose body media type matches. `@PostMapping(path = "/users", consumes = MediaType.APPLICATION_JSON_VALUE)` only handles POSTs with `Content-Type: application/json`. If the path+verb match but the Content-Type does not, Spring returns **415 Unsupported Media Type**. Use it when a handler only knows how to parse one body format, or to split one path into JSON vs. multipart handlers. **`produces` — matches Accept and sets response type.** It narrows a mapping to requests whose `Accept` header can accept one of the listed types AND declares the response's `Content-Type`. `@GetMapping(path = "/users/{id}", produces = MediaType.APPLICATION_JSON_VALUE)` matches `Accept: application/json` (or `*/*`) and stamps the response as JSON. If the client's Accept cannot be satisfied, Spring returns **406 Not Acceptable**. **Multiple values, wildcards, negation.** Both take arrays: `produces = {"application/json", "application/xml"}`. Wildcards work: `consumes = "application/*"` or `"*/*"`. Negation is supported with `!`: `consumes = "!application/json"`. For `produces`, a `charset` may be attached, e.g. `"application/json;charset=UTF-8"`. **Routing on media type.** Because these are part of the request-matching predicate, you can register two handlers with the *same* path and HTTP method that differ only by `produces` — Spring picks the one whose media type best matches the client's Accept header. This is true server-driven content negotiation at the mapping layer, distinct from the `ContentNegotiationConfigurer` (which decides how the requested type is *derived* — Accept header vs. URL extension vs. query param). **Gotchas.** - `consumes` only applies to requests that actually have a body/Content-Type; a GET with no body sails past a `consumes` predicate — don't rely on it to block GETs. - 415 vs 406 confusion: 415 = server can't read what you SENT (Content-Type/`consumes`); 406 = server can't produce what you ASKED FOR (Accept/`produces`). - If `produces` is set and the client sends no Accept header (or `*/*`), it matches and uses the declared type. - Class-level `consumes`/`produces` are *overridden*, not merged, by method-level ones. - Ordering: media-type predicates are evaluated as part of best-match selection, so a more specific media type wins over a wildcard. **When to use.** Set `produces` on REST GETs to lock the response format and enable JSON/XML negotiation; set `consumes` on write endpoints to reject bodies you can't parse and to separate multipart uploads from JSON.
- A client POSTs XML to a handler declared consumes = "application/json". What status code comes back?415 Unsupported Media Type. The path and verb matched but the Content-Type didn't satisfy consumes, so no handler accepts the body.
- What's the difference between produces and the ContentNegotiationConfigurer?produces is a per-handler matching predicate on the Accept header. ContentNegotiationConfigurer configures HOW the requested media type is resolved globally (Accept header, path extension, or a query parameter) before that matching happens.
saying these in an interview costs you the question
- Swapping the meaning of consumes and produces
- Thinking a 406 is returned for a bad Content-Type (it's 415)
- Believing consumes blocks GET requests with no body
- Assuming class-level and method-level media types are merged