Skip to main content

Content-Type Negotiation

RestClient supports multi-format content negotiation: it automatically sets Accept and Content-Type headers based on its configured serializer/parser registry, and selects the right parser for each response based on the server's Content-Type reply.

See Serializer Sets and Parser Sets for the underlying registry API.


Default behavior (single-format JSON)​

With no explicit configuration, RestClient uses JsonSerializer / JsonParser and sends Accept: application/json on every request:

RestClient client = RestClient.create();

MyBean bean = client.get("/api/item/1")
.run()
.body().as(MyBean.class); // parsed with JsonParser

Configuring a multi-format client​

Register multiple serializers and parsers at build time using RestClient.Builder:

RestClient.Builderserializer(Serializer...) — append serializers (built into a SerializerSet)parser(Parser...) — append parsers (built into a ParserSet)serializers(SerializerSet) — supply a pre-built serializer registryparsers(ParserSet) — supply a pre-built parser registrydefaultSerializer(Serializer) — fallback when format is not otherwise discernibledefaultParser(Parser) — fallback when response Content-Type is absent or unmatched
RestClient client = RestClient.builder()
.serializer(JsonSerializer.DEFAULT, CborSerializer.DEFAULT, YamlSerializer.DEFAULT)
.parser(JsonParser.DEFAULT, CborParser.DEFAULT, YamlParser.DEFAULT)
.defaultSerializer(JsonSerializer.DEFAULT)
.defaultParser(JsonParser.DEFAULT)
.build();

RestClient derives the Accept header automatically from all registered parsers:

Accept: application/json, application/cbor, application/yaml

How the client selects a serializer for outbound bodies​

When you call RestRequest.body(Object) (or the typed body helpers), the client needs a serializer to convert the POJO to bytes. Selection order:

  1. If the request carries an explicit Content-Type header, the matching serializer from the SerializerSet is used.
  2. If no explicit Content-Type is set, the defaultSerializer is used (falls back to JsonSerializer.DEFAULT if none configured).

Override the format for one request by setting the Content-Type header:

client.post("/api/items")
.body(myBean)
.header("Content-Type", "application/cbor")
.run()
.assertStatus().asCode().is(201);

How the client selects a parser for inbound responses​

When you call ResponseBody.as(Class), the client reads the response Content-Type header and calls RestClient.getMatchingParser(contentType). Selection order:

  1. The ParserSet is queried for a parser whose media type matches the response Content-Type.
  2. If no match is found, defaultParser is used.
  3. If defaultParser is null, the first parser in the registry is used.
  4. If the registry is empty, JsonParser.DEFAULT is used as a last resort.
// Server responds with Content-Type: application/cbor
MyBean bean = client.get("/api/item/1")
.run()
.body().as(MyBean.class); // → CborParser selected automatically

Overriding the Accept header per request​

Force the server to respond in a specific format by setting the Accept header on the request:

MyBean bean = client.get("/api/item/1")
.header("Accept", "application/yaml")
.run()
.body().as(MyBean.class); // parsed with YamlParser

The per-request header takes precedence over the client-wide default derived from the ParserSet.


Using a pre-built SerializerSet / ParserSet​

For fine-grained control over media-type ordering, q-factors, or reuse across clients, build the registry objects directly:

SerializerSet serializers = SerializerSet.create()
.add(JsonSerializer.DEFAULT, CborSerializer.DEFAULT)
.build();

ParserSet parsers = ParserSet.create()
.add(JsonParser.DEFAULT, CborParser.DEFAULT)
.build();

RestClient client = RestClient.builder()
.serializers(serializers)
.parsers(parsers)
.build();

See Serializer Sets and Parser Sets for the full registry API.


Quick reference: negotiation flow​

StepWhat happens
Client sends requestAccept header = union of all registered parser media types
Client sends bodyContent-Type = matched serializer's media type (explicit header wins)
Server repliesContent-Type response header drives parser selection in getMatchingParser()
FallbackdefaultParser → first registered parser → JsonParser.DEFAULT

See also​