Request binding¶
FromBody<T>, FromQuery<T>, and FromParams<T> are typed alternatives to
reading HttpRequest::body, HttpRequest::query, and HttpRequest::params
by hand. Declare a parameter of one of these wrapper types in a handler and
the router resolves it before the handler runs, handing back a small value
shell that holds either the parsed payload or an error describing why the
bind failed.
The three wrappers live in:
Common shape¶
All three wrappers expose the same surface:
| Member | Type | Meaning |
|---|---|---|
value |
T |
Parsed payload, default-constructed on bind failure. Public member. |
error |
std::optional<…> |
Populated only on failure; std::nullopt on success. |
isOk() |
bool |
!error.has_value(). |
Error() / getError() |
std::optional<…> |
Returns a copy of the bind error. Named Error() on FromBody and getError() on FromQuery / FromParams to mirror the JsonBodyResult convention. |
value is intentionally a public data member — there is no Value() accessor
wrapping it. Read it directly as req.value.field once isOk() returns
true.
When the bind fails, the router short-circuits the handler and writes a
structured error response itself (typically 400 Bad Request with a JSON or
plain-text body, or 415 Unsupported Media Type for a non-JSON FromBody).
Handlers therefore only run when the payload is parseable; they may still
inspect error to react to a partial parse.
FromBody<T>¶
FromBody<T> parses the request body as JSON into a reflectable struct T
through parseJson. The router runs
bindFromBody before the handler; on success the handler is invoked with
isOk() == true and value holding the parsed T.
The bind fails (and the handler is not called) when:
- The
Content-Typeheader is present and is notapplication/json(case-insensitive). The bind reports415 Unsupported Media Type. - The body is missing a required field, has a wrong type, or is not valid
JSON. The bind reports
400 Bad Requestwith a per-field error message viaJsonBodyResult.
| src/main.cpp | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
T must satisfy the same constraints as every other JSON-deserialised type
in Baldr: every non-static data member must be std::string,
std::string_view, an integral type, double, float, or bool; or a
specialisation of baldr::detail::readJsonField must exist for it. See
Parsing JSON bodies for the full list.
FromQuery<T>¶
FromQuery<T> aggregates the parsed query string (HttpRequest::query) into
a reflectable struct. Each non-static data member of T is looked up by
name in the query map; the bind fails with 400 Bad Request if any member
is missing or cannot be parsed as the member's type.
| src/main.cpp | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 | |
Booleans accept "true", "false", "1", and "0". Numeric members use
std::stod / std::stoll on the raw value and reject partial parses
(e.g. "42abc" fails for int).
FromParams<T>¶
FromParams<T> aggregates path parameters (the segments declared with
:name in the route template) into a reflectable struct. The bind is
otherwise identical to FromQuery<T> — every member of T must have a
matching :name segment and parse as the member's type, otherwise the
bind fails with 400 Bad Request.
| src/main.cpp | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Combining wrappers¶
A handler may declare any combination of FromBody, FromQuery, and
FromParams parameters, in any order, alongside injected services. The
router resolves each wrapper independently and short-circuits the whole
handler if any of them fails to bind.
| src/main.cpp | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
The same code path is implemented by
RouteRegistration::Handle
— see Route options for the surrounding route metadata.
OpenAPI integration¶
The framework reflects on the handler's signature at registration time and
derives an OpenAPI requestBody / parameters block from any
FromBody<T>, FromQuery<T>, or FromParams<T> parameter it finds. This is
covered in detail in OpenAPI metadata; the short
version is: declaring the wrapper in the signature is enough — no extra
configuration is required for the generated spec to expose the request
schema.
Use the explicit WithRequestType<T>() / WithQueryType<T>() /
WithPathType<T>() builders only when the handler argument is a bare
HttpRequest& or you need to override the inferred schema.