skip to content

In REST Assured, how do accept(ContentType.JSON) and contentType(ContentType.JSON) differ in the header they write?

level: middleimportance: nice to knowfreq 34%

answer

  1. both are just header() calls
  2. one enum, several media types
  3. toString versus getAcceptHeader
  4. accept widens, contentType does not

basics

~10 s

Both write a plain request header, but accept(ContentType.JSON) expands the enum into all four of its media types - application/json, application/javascript, text/javascript and text/json - while contentType(ContentType.JSON) writes only the first, application/json.

solid answer

~40 s

Both are wrappers over `header(...)`: `contentType(...)` writes `Content-Type`, `accept(...)` writes `Accept`. The difference is how each renders a `ContentType` constant. `contentType(ContentType.JSON)` uses the enum's `toString()`, which is the first media type only, so a dispatch call sends `Content-Type: application/json`. `accept(ContentType.JSON)` uses `getAcceptHeader()`, which joins every media type the constant carries, so it sends `Accept: application/json, application/javascript, text/javascript, text/json`. Pass a `String` to either one when you need the header to be exactly what you typed. Because both are headers, they also inherit header merging — and `HeaderConfig` marks `content-type` and `accept` as the two names that overwrite by default, which is why calling either twice replaces instead of duplicating.

code

java · 26 lines
java
import io.restassured.http.ContentType;

import static io.restassured.RestAssured.given;

class DispatchMediaTypeTest {

    void listRoutes() {
        given()
            .accept(ContentType.JSON)
        .when()
            .get("/v1/routes")
        .then()
            .statusCode(200);
    }

    void dispatchPlough() {
        given()
            .contentType(ContentType.URLENC)
            .formParam("ploughId", "PLW-114")
            .formParam("routeId", "RT-7")
        .when()
            .post("/v1/dispatches")
        .then()
            .statusCode(202);
    }
}

go deeper

for a junior

Know that accept() sets the Accept header and contentType() sets Content-Type, and that both take either a ContentType constant or a plain string. The expansion detail can wait.

for a middle

Explain that a ContentType constant carries several media types, that accept() joins all of them while contentType() takes the first, and that both are ordinary header writes underneath.

for a senior

Bring the consequence: a test or filter that asserts on the outgoing Accept header must use the string overload, and a request log showing four media types is the library, not a stray specification.

for a principal

Set the house rule on which overload teams use, so requests are reproducible and auditable across suites rather than depending on whichever media types a library constant happens to bundle.

## Two methods, one underlying mechanism `RequestSpecification.accept(...)` and `RequestSpecification.contentType(...)` are not special machinery. Both are thin wrappers that end up calling `header(...)`: `accept(...)` writes the `Accept` header and `contentType(...)` writes `Content-Type`. Everything interesting about them is what value they put there when you hand them a `ContentType` enum constant rather than a string. `io.restassured.http.ContentType` has **eight** constants — `ANY`, `TEXT`, `JSON`, `XML`, `HTML`, `URLENC`, `BINARY`, `MULTIPART` — and each constant carries a *list* of media-type strings, not one. `ContentType.JSON` holds four: `application/json`, `application/javascript`, `text/javascript`, `text/json`. ## The asymmetry - `contentType(ContentType.JSON)` stringifies the constant, and the enum's `toString()` returns the **first** entry only. The header sent is `Content-Type: application/json`. - `accept(ContentType.JSON)` calls the constant's `getAcceptHeader()`, which joins **all** the entries with `", "`. The header sent is `Accept: application/json, application/javascript, text/javascript, text/json`. - `accept(String mediaTypes)` writes exactly the string you pass, so `accept("application/json")` sends that one media type and nothing else. - `contentType(String)` likewise writes exactly what you pass. | call | header written for a dispatch API call | |---|---| | `contentType(ContentType.JSON)` | `Content-Type: application/json` | | `accept(ContentType.JSON)` | `Accept: application/json, application/javascript, text/javascript, text/json` | | `accept(ContentType.XML)` | `Accept: application/xml, text/xml, application/xhtml+xml` | | `accept(ContentType.ANY)` | `Accept: */*` | | `accept("application/json")` | `Accept: application/json` | The design makes sense once you see it: a `Content-Type` describes exactly one representation you are sending, so one value is correct; an `Accept` header advertises everything you are willing to read, so widening it is helpful. It is still a surprise the first time a `GET /v1/routes` on a snowplough dispatch API logs four media types you never typed. ## Why it matters in practice 1. **Assertions on your own request.** If a test or a filter inspects the outgoing `Accept` header and compares it to `"application/json"`, the enum form fails the comparison and the string form passes. 2. **Strict servers.** A dispatch service that parses `Accept` narrowly, or that logs the raw header for auditing, will show the widened list. Nothing is wrong, but the audit trail is noisier than the test source suggests. 3. **Debug reading.** When the request log shows a four-value `Accept` for a one-line `accept(JSON)` call, knowing why saves you hunting for a rogue filter or spec. ## The other thing `contentType()` does `contentType(...)` also flips an internal "content type is allowed" flag on the specification. Its counterpart, `noContentType()`, clears that flag *and* removes the `Content-Type` header, which is how you send a request with genuinely no content type — useful when you are testing what the dispatch API does with a malformed submission. Setting a content type explicitly also suppresses the type REST Assured would otherwise infer from what you attached to the request. Separately, `EncoderConfig` can append a default charset to the content type on the way out, so the header on the wire is not always byte-identical to the constant you named. ## The merge rule that follows from all this Because both methods are just headers, they inherit header behaviour — and header behaviour is to **merge** same-name values by default. That would be terrible for these two, so `HeaderConfig` ships with exactly two names marked for overwrite: `content-type` and `accept`, matched case-insensitively. That is why: - Calling `contentType(...)` twice replaces rather than sending two `Content-Type` headers. - Calling `accept(...)` twice replaces rather than concatenating two `Accept` headers. - Calling `header("X-Depot-Id", ...)` twice sends the value twice, because that name is not in the overwrite set. - `HeaderConfig.mergeHeadersWithName("Accept")` deliberately un-does the exception if you ever need two `Accept` lines in one request. ## Which constants actually widen Not every constant expands, which is why the behaviour goes unnoticed for so long. Five of the eight carry a single media type: - `ANY` is `*/*`, and `TEXT` is `text/plain`. - `HTML` is `text/html`. - `URLENC` is `application/x-www-form-urlencoded`. - `BINARY` is `application/octet-stream`. Three carry more than one: - `JSON` carries four, the first being `application/json`. - `XML` carries three, the first being `application/xml`. - `MULTIPART` carries ten, the first being `multipart/form-data`. So the divergence between the two methods is confined to `accept(ContentType.JSON)`, `accept(ContentType.XML)` and `accept(ContentType.MULTIPART)`. For the other five constants the two calls write the same string, and a team can use both for months on a dispatch suite without ever seeing the difference — until someone asserts on the outgoing `Accept` header. ## What to say in an interview Lead with the mechanism: both calls are `header(...)` in disguise. Then give the asymmetry — `toString()` for the content type, `getAcceptHeader()` for accept — and the concrete four-value expansion of `ContentType.JSON`. Finish with the practical rule: pass the enum when you want REST Assured's breadth, pass a string when you need the header to be exactly one media type, and never assert on the enum form expecting a single value.

  • How would you make a REST Assured request send exactly one media type in its Accept header?
    Pass a string instead of the enum: `accept("application/json")` writes that value verbatim. The `ContentType` overload deliberately widens to every media type the constant carries, so it is the wrong tool when a test asserts on the outgoing header or a service parses `Accept` strictly.
  • What does noContentType() do on a REST Assured request specification?
    It removes the `Content-Type` header and clears the internal flag that lets REST Assured supply one, so the request goes out with no content type at all. It is how you test what a dispatch endpoint does with an unlabelled submission instead of letting the library infer a type for you.

saying these in an interview costs you the question

  • Assumes accept(ContentType.JSON) sends only application/json
  • Thinks accept() and contentType() are aliases for one header
  • Believes a ContentType constant holds exactly one media type
  • Says calling contentType() twice sends two Content-Type headers
  • Claims accept() performs server-side negotiation rather than writing a header