Request Bodies
A request body is attached with the builder’s body
function. The type of the value decides three things: the Content-Type header,
whether the body is sent with a known Content-Length or with chunked transfer
encoding, and how the bytes are produced on the wire. This section covers the
built-in body types, extending shows how to add
your own type.
How the Body Is Framed
body accepts any value for which a conversion is
defined, by calling tag_invoke with a body_from_tag<T>. The result is an
any_request_body.
Two rules hold for every body type:
-
The body’s
Content-Typeis sent unless the request already sets that header explicitly. Settinghttp::field::content_typeon the request always wins. -
The framing — a
Content-Lengthheader, or chunked transfer encoding — is always taken from the body and cannot be overridden. A body whose size is known in advance is sent with aContent-Length; one whose size is not is sent chunked.
Strings
A std::string, a std::string_view, or a string literal becomes a
body with Content-Type: text/plain; charset=utf-8 and a Content-Length:
auto r = co_await client.post("https://example.com/post")
.body("plain text payload")
.send();
A std::string body owns its data. A std::string_view or
string-literal body refers to the characters in place without copying, so the
underlying buffer must outlive the request. To send text under a different media
type, set the header explicitly:
auto r = co_await client.post("https://example.com/post")
.body("<note>hi</note>")
.header(http::field::content_type, "application/xml")
.send();
JSON
The Boost.JSON types — json::value, json::object, json::array, and
json::string — are serialized incrementally as the request is sent, under
Content-Type: application/json.
json::object obj({ { "user", "John" }, { "lang", "En" } });
auto r = co_await client.post("https://example.com/post")
.body(obj)
.as<json::value>();
A literal value can be constructed inline by naming the type explicitly:
auto r = co_await client.post("https://example.com/post")
.body<json::array>({ 1, 2, 3 })
.as<json::value>();
URL-Encoded Forms
urlencoded_form builds an application/x-www-form-urlencoded body from
name and value pairs. Names and values are percent-encoded, with spaces written
as +. append chains:
auto r = co_await client.post("https://example.com/post")
.body(burl::urlencoded_form()
.append("user", "John")
.append("lang", "En"))
.as<json::value>();
Multipart Forms
multipart_form builds a multipart/form-data body, the format browsers
use for file uploads. It mixes text parts and file parts, separated by a
randomly generated boundary:
auto r = co_await client.post("https://example.com/upload")
.body(burl::multipart_form()
.text("priority", "high")
.file("attachment", "./report.log"))
.as<json::value>();
A file part is streamed from disk while the request is sent; only the path is
held in the form. The filename and Content-Type reported in the part header
are deduced from the path — falling back to application/octet-stream — and
either can be given explicitly. When the contents are already in memory but
should still be presented to the server as a file,
bytes writes a part with a filename from an in-memory
buffer.
Files
Naming std::filesystem::path as the body type uploads a file. Its contents
are streamed while the request is sent, so the whole file is never held in
memory:
auto r = co_await client.put("https://example.com/put")
.body<std::filesystem::path>("./report.log")
.send();
The Content-Type is deduced from the filename extension, falling back to
application/octet-stream, and the Content-Length is the file’s size at the
time of the call. Because the length is fixed up front, a file that shrinks
before the transfer completes fails the request with error::file_changed,
rather than sending a truncated or mis-framed body.
Next Steps
-
Responses — Reading the body that comes back
-
Extending — Teaching
bodyabout your own types -
Headers and Query Parameters — Overriding the
Content-Type