In REST Assured, why does body(entry) throw for content type application/vnd.divelog.v3, and what fixes it?
answer
- substring match, not media-type parsing
- a plus-json suffix already works
- the encoder map makes it count
- throws before the socket opens
basics
~20 sREST Assured chooses a writer by looking for the substring json or xml inside the request content type. application/vnd.divelog.v3 contains neither, so body(Object) throws IllegalArgumentException before anything is sent. EncoderConfig.encodeContentTypeAs maps that type to ContentType.JSON, and the JSON scan then runs.
solid answer
~40 sThe mapping layer's JSON branch fires when the lower-cased request content type **contains** `json`, or when `EncoderConfig`'s encoder map says that type should be treated as `ContentType.JSON`; the XML branch works the same way with `xml`. A vendor type spelled `application/vnd.divelog.v3+json` already contains `json` and needs no configuration at all. One spelled `application/vnd.divelog.v3` matches nothing, falls through both branches and throws `IllegalArgumentException` saying it cannot determine how to serialize that content type — client-side, before a socket is opened. Register it with `encoderConfig().encodeContentTypeAs("application/vnd.divelog.v3", ContentType.JSON)` and the ordinary JSON scan applies. The lookup lower-cases the request's content type and strips the charset, so register the key lower-case and without one. Mapping it to a family no serializer supports, such as `ContentType.BINARY`, still throws; the message merely gains *as BINARY*.
code
java · 16 linesimport static io.restassured.config.EncoderConfig.encoderConfig;
import static io.restassured.RestAssured.given;
import io.restassured.config.RestAssuredConfig;
import io.restassured.http.ContentType;
RestAssured.config = RestAssuredConfig.config().encoderConfig(
encoderConfig().encodeContentTypeAs(
"application/vnd.divelog.v3", ContentType.JSON));
given()
.contentType("application/vnd.divelog.v3")
.body(entry) // now reaches the JSON scan
.when()
.post("/clubs/kraken-divers/dives")
.then()
.statusCode(201);go deeper
Know that a content type REST Assured does not recognise stops the request before it is sent, and that the message names the content type it could not place. Reading that message is most of the fix.
Explain the substring test and the encoder map as the two ways a content type reaches the JSON or XML branch, and why a +json suffix needs no configuration.
Show where the registration belongs in a suite that speaks a versioned vendor media type, and how you would tell this local failure apart from a genuine rejection by the service.
Argue the upstream fix: publishing structured +json media types removes a class of client-side configuration entirely, which matters when many teams write clients against the same API.
## How the branch is chosen REST Assured does not parse the request content type as a structured media type. It lower-cases the value, strips anything after a `;`, and then asks two questions in order: - Does the remaining string **contain** the substring `json`? If yes, run the JSON branch. - Does it contain the substring `xml`? If yes, run the XML branch. Only if both answers are no does it consult `EncoderConfig`. The config carries a map from a content type to a `ContentType` constant, populated by `encodeContentTypeAs(String, ContentType)`, and a hit in that map sends the request down the corresponding branch as if the substring had been there. `application/vnd.divelog.v3` contains neither word and has no entry in that map, so both tests fail and both branches are skipped. The routine falls to its final `else` and throws `IllegalArgumentException`, with a message saying it cannot determine how to serialize content type `application/vnd.divelog.v3`. ## When you need `encodeContentTypeAs` and when you do not The substring test is generous, which is why most vendor types work with no configuration at all: - `application/vnd.divelog.v3+json` contains `json`. It works untouched. - `application/dive-log+xml` contains `xml`. It works untouched. - `text/json` and `application/javascript` — both listed under `ContentType.JSON` — contain `json` and `javascript` respectively, and only the first reaches the JSON branch by substring. - `application/vnd.divelog.v3` contains neither. This is the case that needs configuration. So the rule of thumb is simple: if your media type carries a structured `+json` or `+xml` suffix, there is nothing to do. If your API invented a bare vendor type, you must tell REST Assured which family it belongs to before a body can be written for it. ## Registering the type `EncoderConfig.encodeContentTypeAs(contentType, encoder)` returns a new `EncoderConfig` — the config objects are immutable — carrying one extra entry in the map. Install it on `RestAssuredConfig` and the JSON branch, with its usual Jackson 3, Jackson 2, Jackson 1, Gson, Johnzon, Yasson order, applies to that content type. Two matching details decide whether the entry is ever found: 1. The lookup key is the **lower-cased** request content type, so register the key in lower case. An entry stored as `application/vnd.DiveLog.v3` will never match. 2. The lookup strips the charset first, so register `application/vnd.divelog.v3` and not `application/vnd.divelog.v3; charset=UTF-8`. The request may still carry a charset; it is removed before the map is consulted. Note also that this configures the **writer** only. It changes which serializer produces an outgoing body; it has no effect on how a response with that content type is read back. ## Reading the two exception messages The mapping layer distinguishes "I know the family but have no library" from "I do not know the family", and the message tells you which one you hit: | situation | exception | what it means | |---|---|---| | content type contains `json`, no JSON mapper present | `IllegalStateException` | add Jackson, Gson, Johnzon or Yasson | | content type contains `xml`, no XML mapper present | `IllegalStateException` | add a JAXB or Jakarta EE mapper | | content type matches nothing | `IllegalArgumentException` | register it with `encodeContentTypeAs` | | registered to a family with no serializer | `IllegalArgumentException` | the message adds *as BINARY* or similar | That last row is worth trying once. Mapping a custom type to something like `ContentType.BINARY` does not fail at configuration time; it fails at `body(...)` with the same message extended to say that no serializer supports that format. The map only redirects a type into an existing JSON or XML branch — it cannot invent a writer. ## Where this bites in a suite All of these failures happen client-side, while the specification is still being assembled. No socket is opened, so there is no server response and no status code to inspect; a stack trace with no HTTP interaction in it is the signature. That makes the fix cheap once you recognise it, and it also means the failure is deterministic — it does not depend on the environment under test. The practical consequence for a dive-log suite that speaks a versioned vendor type is that the encoder registration belongs in the same shared setup as the base URI, registered once for every media type the API accepts: - Register each bare vendor request type against `ContentType.JSON` or `ContentType.XML`. - Prefer publishing types with a `+json` suffix in the API itself, which removes the need entirely. - Keep the registrations lower-case and charset-free so they actually match.
- Does application/vnd.divelog.v3+json need encodeContentTypeAs as well?No. The check is a case-insensitive substring test, so any content type whose name contains `json` — including one carrying a structured `+json` suffix — already reaches the JSON branch. The configuration is needed only for a type whose spelling contains neither `json` nor `xml`.
- What error do you get when you map a custom type to ContentType.BINARY?`body(Object)` still throws `IllegalArgumentException`, but the message extends to say it cannot determine how to serialize that content type *as BINARY (no serializer supports this format)*. The encoder map only redirects a type into an existing JSON or XML branch; it cannot supply a writer for a family REST Assured has none for.
saying these in an interview costs you the question
- Assumes REST Assured parses the media type structurally rather than by substring
- Thinks an unrecognised content type silently falls back to JSON
- Expects the service to answer with an error rather than a local exception
- Registers the encoder key with a charset or in mixed case
- Believes encodeContentTypeAs also changes how a response is read