skip to content

In a Karate feature file, what body does each of `request`, `form field` and `multipart field` build, and what `Content-Type` does each send when you never set that header yourself?

level: middleimportance: should knowfreq 47%

answer

  1. Three keywords, three wire formats
  2. One of them needs no header at all
  3. The body's runtime type decides the header
  4. urlencoded versus form-data versus raw

basics

~20 s

request sets the body as-is and Karate infers the Content-Type from its data type: application/json for a map or list, text/plain for a string, application/xml for an XML node, octet-stream for bytes. form field builds a urlencoded body, multipart field a multipart/form-data one.

solid answer

~40 s

The three keywords produce three different bodies. **`request`** takes the evaluated expression as the whole body and, if you set no `Content-Type` header, Karate derives one from the value's runtime type — a map or list becomes `application/json`, a string `text/plain`, an XML node `application/xml`, a byte array `application/octet-stream`. **`form field`** accumulates key/value pairs into an `application/x-www-form-urlencoded` body. **`multipart field`** and **`multipart file`** accumulate parts into a `multipart/form-data` body with a generated boundary. `form field` and `multipart field` share one underlying part builder, so whichever of the two you use *first* decides which of those two encodings the call sends. One more edge is worth knowing: if the verb resolves to GET, accumulated form fields are drained into the query string instead of a body.

code

gherkin · 18 lines
gherkin
Scenario: three bodies, three content types
  * url 'https://api.example.com'

  # application/json, inferred from the map
  Given path 'cats'
  And request { name: 'Billie' }
  When method post

  # application/x-www-form-urlencoded
  Given path 'oauth', 'token'
  And form field grant_type = 'client_credentials'
  When method post

  # multipart/form-data with a generated boundary
  Given path 'cats', 1, 'photo'
  And multipart file image = { read: 'billie.png', contentType: 'image/png' }
  And multipart field caption = 'ceiling cat'
  When method post

go deeper

for a junior

Learn the mapping first: request for a JSON or XML document, form field for a login or token endpoint, multipart file for an upload.

for a middle

Explain that the content type is inferred from the body value's runtime type and that the first of form field or multipart field fixes the encoding for the call.

for a senior

When a service answers 415 or 400 on a request that looks right, compare the keyword used against the format the endpoint documents before suspecting the payload.

for a principal

Worth standardising across a suite: one convention for token and upload endpoints stops each team rediscovering the urlencoded-versus-form-data distinction the hard way.

## Three keywords, three bodies | Keyword | Body it builds | Default `Content-Type` | |---|---|---| | `request` | the evaluated value, as-is | inferred from the value's type | | `form field` / `form fields` | accumulated key/value pairs | `application/x-www-form-urlencoded` | | `multipart field` / `multipart file` / `multipart entity` | accumulated parts | `multipart/form-data; boundary=...` | They are not interchangeable spellings of "set the body". Choosing the wrong one is a wire-format mistake that a server usually answers with a 415 or a 400, and the step itself gives no hint. ## `request` and the inferred content type The most-used Karate idiom is to write the body as inline JSON with unquoted keys and let the type do the work: ```gherkin Given url 'https://api.example.com' And path 'cats' And request { name: 'Billie', type: 'LOL' } When method post Then status 201 ``` No `Content-Type` header was set, and the request still goes out as JSON. The rule is a plain mapping from the runtime type of the body value: - a map or a list — `application/json` - a string — `text/plain` - an XML node — `application/xml` - a byte array — `application/octet-stream` - anything else — no inferred type This is why setting the header explicitly is usually unnecessary, and why it is essential in one case: a body that *is* a string but must be sent as something else. `request '{"name":"Billie"}'` — quoted, so a string — infers `text/plain`. If the server insists on `application/json`, either drop the quotes so it evaluates to a map, or set the header yourself. The body value is an expression, so `request read('cat.json')`, `request myVariable` and inline XML all work the same way. Setting the header yourself always wins: the inference runs only when no `Content-Type` is already present on the request, so an explicit `header Content-Type = 'application/vnd.api+json'` step suppresses it entirely. That is the escape hatch for a service that insists on a vendor media type. ## `form field` versus `multipart field` Both accumulate named values, and both are additive — each step adds one entry, so several steps build one body: ```gherkin Given path 'oauth', 'token' And form field grant_type = 'client_credentials' And form field scope = 'cats.read' When method post Then status 200 ``` That sends `grant_type=client_credentials&scope=cats.read` as `application/x-www-form-urlencoded`. Swapping those two steps to `multipart field` sends the same names as parts of a `multipart/form-data` body with a generated boundary — a different wire format that a token endpoint will reject. The pair share one underlying part builder, and the builder is created by whichever keyword touches it first, with the multipart flag already decided. **Mixing `form field` and `multipart field` in one call does not produce a hybrid**: the first keyword used chooses the encoding for the whole body, and the later steps just add entries to it. If you find both spellings in one scenario, that is a bug to fix, not a style choice. `multipart file` is the variant for an attachment; it takes a map so that the part can carry a filename and its own content type alongside the value, which is what a file upload needs and a plain field cannot express. ## The GET drain One behaviour surprises people who reuse a block of steps across verbs. When the request is finally built, if the verb has resolved to **GET** and form fields are present, those fields are moved into the query string and the form body is dropped. So a block of `form field` steps that posts correctly will, on `method get`, quietly send `?grant_type=client_credentials&scope=cats.read` and no body at all. The related default: if no `method` step ran but multipart parts are present, the verb defaults to POST rather than GET — the only case where the verb is inferred rather than stated. ## Choosing between them 1. Sending a JSON or XML document — `request`, and let the type infer the header. 2. Sending an HTML-form-style payload, most often a token or login endpoint — `form field`. 3. Uploading a file, or mixing a file with metadata fields — `multipart file` plus `multipart field`. And whichever you pick, remember that the body, the form parts and any explicit `Content-Type` header are all cleared once the call fires. A second call in the same scenario has to build its body again from scratch.

  • What happens if a scenario uses both `form field` and `multipart field` before the same `method` step?
    It does not produce a hybrid body. The two keywords share one part builder, created by whichever runs first with the multipart flag already fixed, so the first keyword used decides the encoding for the whole body and the later steps merely add entries to it. Treat a scenario containing both spellings for one call as a bug.
  • Why does `request '{"name":"Billie"}'` sometimes get rejected as the wrong content type?
    Because the value is a string, and Karate infers `text/plain` from a string body. Drop the quotes so the expression evaluates to a map and the inferred type becomes `application/json`, or set `header Content-Type = 'application/json'` explicitly for that call.
  • What happens to accumulated `form field` values if the call is fired with `method get`?
    They are drained into the query string and the form body is dropped, so the request goes out with those names and values as query parameters and no body. It is worth knowing when a block of steps is reused across verbs, because nothing warns you that the payload changed shape.

saying these in an interview costs you the question

  • Thinks form field and multipart field are the same encoding
  • Sets Content-Type by hand for every JSON body
  • Expects a quoted JSON string to be sent as JSON
  • Believes mixing form and multipart yields a hybrid body
  • Uses multipart field to upload a file with a filename
  • Assumes the body survives into the next call