skip to content

Bodies and Multipart Uploads

Attaching an entity to a request: the body() overloads for a string, byte array, file or stream, and the multiPart() family with its builder and the control-name and boundary defaults it supplies.

part ofREST Assuredoverview, primer and where to startread it →
on this pageshow

questions

4

In REST Assured, what control name and Content-Type does given().multiPart(new File(...)) produce?

level: juniorimportance: must knowfreq 62%

answer

  1. think of an HTML file input
  2. the control name is the field name
  3. MultiPartConfig holds the default
  4. File overloads reuse File.getName()

basics

~20 s

Passing a File alone attaches one part under MultiPartConfig's default control name, file, using the file's own name as the filename. Its part type defaults to application/octet-stream. REST Assured also sets the request Content-Type to multipart/form-data by itself.

solid answer

~50 s

`given().multiPart(new File("harris-weights.csv"))` appends one entry to the request's part list and nothing more — you never call `contentType()` yourself, because once that list is non-empty REST Assured composes the request type as `multipart/` plus `MultiPartConfig.defaultSubtype()`, which ships as `form-data`. The **control name** is the form-field name the endpoint binds the part to, and the one-argument overload has nowhere to read it from, so it falls back to `MultiPartConfig.defaultControlName()` — the literal string `file`. The filename comes from `File.getName()` and the part's own type defaults to `application/octet-stream`. Name the control yourself with `multiPart("scalePhoto", file)` or `multiPart("scalePhoto", file, "image/jpeg")`. `RequestSpecification` declares **13 `multiPart` overloads** in total, and every call appends another part, so uploads stack in call order. Where an endpoint wants a different field name, prefer naming it at the call site over moving the config default, which silently retunes every bare `multiPart(File)` in the suite.

code

java · 14 lines
java
import java.io.File;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

// No contentType() call: multiPart() sets multipart/form-data itself
given()
    .multiPart(new File("scale-2026-09-07.jpg"))                    // control name "file"
    .multiPart("weighInLog", new File("harris-weights.csv"), "text/csv")
.when()
    .post("/hawks/HW-118/weigh-ins/WI-9032/attachments")
.then()
    .statusCode(201)
    .body("storedParts", equalTo(2));

go deeper

for a junior

Be ready to write the two-line upload from memory: multiPart with a File, then post, then a status assertion. Know that the bare one-argument form uses the control name file.

for a middle

Explain where each default comes from — MultiPartConfig for the control name, File.getName() for the filename, multipart/form-data for the request type — rather than just naming the values.

for a senior

Show judgement about naming the control per call instead of moving the config default, and be able to say why an upload test that fails with a 400 is usually a control-name mismatch.

for a principal

Own the convention: whether upload helpers in the suite pin MultiPartConfig once or spell control names out at every call site, and what that costs a reader tracking a failing upload months later.

## What `multiPart` actually does `RequestSpecification.multiPart(...)` sends nothing by itself. Each call appends one entry to a list the request specification carries, and that list is turned into a payload only when the verb — `post`, `put` or `patch` — fires. Two things follow immediately: a request may carry as many parts as you need, and the call order is the part order. Nothing in the DSL limits you to a single attachment. The moment that list is non-empty, REST Assured takes over the request's `Content-Type`. If you set none, it composes `multipart/` plus `MultiPartConfig.defaultSubtype()`, which ships as `form-data`. So uploading a weigh-in photo to a falconry weight-log API needs no content-type call at all: ```java given().multiPart(new File("scale-2026-09-07.jpg")) .when().post("/hawks/HW-118/weigh-ins/WI-9032/attachments") .then().statusCode(201); ``` That request goes out as `multipart/form-data` with one part. ## The control name, and where its default comes from The **control name** is the field name the receiving endpoint binds the part to — the `name` attribute of the `<input type="file">` in the form the endpoint was written for. Every overload except the one-argument `multiPart(File)` takes it as its first argument. `multiPart(File)` has nowhere to read it from, so it falls back to `MultiPartConfig.defaultControlName()`, whose value is the literal string `file`. That default is configuration, not a constant welded into the call: - `given().config(config().multiPartConfig(multiPartConfig().defaultControlName("scalePhoto")))` changes it for one request. - Assigning the same config to `RestAssured.config` changes it for the whole suite. - `multiPart("scalePhoto", file)` overrides it for one part and is almost always the clearer choice. ## Which overload fills in which default `RequestSpecification` declares **13** `multiPart` overloads. They differ in what content they carry and in which of the three per-part attributes — control name, file name, mime type — you supply versus REST Assured supplies. | overload family | how many | file name comes from | part type default | | --- | --- | --- | --- | | `multiPart(File)` | 1 | `File.getName()` | `application/octet-stream` | | `multiPart(name, File[, mimeType])` | 2 | `File.getName()` | `application/octet-stream` | | `multiPart(name, fileName, byte[][, mimeType])` | 2 | your argument | `application/octet-stream` | | `multiPart(name, fileName, InputStream[, mimeType])` | 2 | your argument | `application/octet-stream` | | `multiPart(name, contentBody[, mimeType])` | 2 | not sent for text content | `text/plain` | | `multiPart(name, Object[, mimeType])` | 2 | not sent for text content | mime type drives the mapper | | `multiPart(name, fileName, Object, mimeType)` | 1 | your argument | your argument | | `multiPart(MultiPartSpecification)` | 1 | the builder, else the config default | per content kind | Two readings of that table matter in practice: - The `byte[]` and `InputStream` families have **no** overload without an explicit file name, because there is nothing to derive one from. - Overload selection happens at compile time on the static type of the argument, so `multiPart("falconerId", "F-4417")` binds to the raw-`String` overload while `multiPart("weighIn", weighInPojo)` binds to the object overload and serializes. ## The builder, for what the overloads do not cover When you need a combination the 13 overloads do not offer — per-part headers, an explicit charset, or no file name at all — build the part instead of calling an overload: 1. `new MultiPartSpecBuilder(content)` accepts an `Object`, `String`, `byte[]`, `InputStream` or `File`. 2. `.controlName(...)` and `.fileName(...)` set those attributes **and mark them explicitly specified**, which is what stops `MultiPartConfig` substituting its own defaults at send time. 3. `.mimeType(...)`, `.charset(...)`, `.header(name, value)` and `.headers(map)` fill in the rest. 4. `.emptyFileName()` is literally `fileName(null)` — an explicit "no file name". 5. `.build()` yields a `MultiPartSpecification` for `multiPart(spec)`. `charset(...)` refuses `byte[]` and `InputStream` content with an `IllegalArgumentException`, since there is no text there to encode. ## Pitfalls worth rehearsing - **Do not add `contentType(ContentType.JSON)` "to be safe".** Once a request has parts, any content type that neither starts with `multipart/` nor contains `multipart+` raises `IllegalArgumentException` before the request leaves the process. - **`body(...)` and `multiPart(...)` do not compose.** With parts present the assembled body content becomes an empty byte array and the multipart encoder rebuilds the payload from the part list, so the `body(...)` argument is silently discarded. - **The control name is the server's contract.** Getting it wrong normally surfaces as a 400 naming a missing field, not as an upload error, so read the endpoint's field name rather than guessing. - **A multipart request still needs a body-bearing verb.** `post`, `put` and `patch` carry parts; a `get` with parts is not what any upload endpoint expects.

  • How would you change the default control name for every bare multiPart(File) call in a suite?
    Assign a config that carries it: `RestAssured.config = config().multiPartConfig(multiPartConfig().defaultControlName("scalePhoto"))`. `MultiPartConfig` is one of the eighteen config objects on `RestAssuredConfig`, so the same value can be scoped per request with `given().config(...)` or baked into a `RequestSpecBuilder.setConfig(...)`. Naming the control per call stays clearer for a suite that talks to more than one endpoint.
  • What happens if the same request calls both given().body(...) and given().multiPart(...)?
    The `body(...)` argument is silently dropped. Once the part list is non-empty REST Assured assembles an empty byte array as the body content, and the registered multipart encoder rebuilds the payload from the parts alone. There is no error and no warning, so a body set for an upload endpoint simply never arrives — send it as another part instead.

The control name is the label on the slot you post something into, not the name written on the envelope. Change the envelope's name all you like; the sorter still reads the slot.

saying these in an interview costs you the question

  • Thinks multiPart() needs an explicit contentType() call to work
  • Says the control name defaults to the uploaded file's own name
  • Believes only one multiPart() call is allowed per request
  • Claims a File part defaults to text/plain rather than octet-stream
  • Assumes body() and multiPart() can both be set on one request
open as a page

In REST Assured, how do given().body()'s String, byte[], File and InputStream overloads differ?

level: middleimportance: must knowfreq 55%

basics

~20 s

All four store the argument verbatim as the request body; no object mapper runs. They differ in the Content-Type REST Assured picks when none is set. A byte array or stream gives application/octet-stream, a String or File text/plain.

open as a page

In REST Assured, which Content-Type and boundary does a multiPart() request actually send?

level: seniorimportance: should knowfreq 33%

basics

~20 s

REST Assured builds the request type as multipart/ plus MultiPartConfig.defaultSubtype(), which ships as form-data. The boundary is resolved in three steps. One already present in your own content type wins, otherwise MultiPartConfig.defaultBoundary(), otherwise a freshly generated random token.

open as a page

Your REST Assured multiPart() upload reaches the server with an empty file name. Why?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Text content carries no file name in REST Assured. String content becomes an Apache StringBody, which has no filename slot, so MultiPartConfig.defaultFileName() and MultiPartSpecBuilder.fileName() are both ignored. Only File, byte array and stream parts carry one.

open as a page