URL encoded
URL encoding, also known as percent encoding, is one
of the formats used by browsers to encode the data submitted via a POST
web form.
You can use UrlEncodedBody<T>
to work with URL encoded payloads:
it parses the raw request body into an instance of the type T
you specified.
use pavex::http::StatusCode;
use pavex::request::body::UrlEncodedBody;
#[derive(serde::Deserialize)]
pub struct HomeListing {
address: String,
price: u64,
}
pub fn handler(body: &UrlEncodedBody<HomeListing>) -> StatusCode {
println!("New home listing at {}, for ${}", body.0.address, body.0.price);
// [...]
}
The whole request body is buffered in memory before being deserialized.
Registration
If you're using the default ApiKit
,
you don't need to register a constructor for UrlEncodedBody<T>
manually:
it's already included in the kit.
If you're not using ApiKit
, you need to register a constructor for UrlEncodedBody<T>
.
You can use UrlEncodedBody::register
to register the default constructor
and error handler:
use pavex::blueprint::Blueprint;
use pavex::request::body::UrlEncodedBody;
pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
UrlEncodedBody::register(&mut bp); // (1)!
// [...]
}
- You also need to register a constructor for
BufferedBody
!
Check out the BufferedBody guide for more details.
Extraction
Inject UrlEncodedBody<T>
as an input in your components to access the parsed body:
use pavex::http::StatusCode;
use pavex::request::body::UrlEncodedBody;
#[derive(serde::Deserialize)]
pub struct HomeListing {
address: String,
price: u64,
}
pub fn handler(body: &UrlEncodedBody<HomeListing>) -> StatusCode {
println!("New home listing at {}, for ${}", body.0.address, body.0.price);
// [...]
}
Deserialization
The newly defined struct must be deserializable—i.e. it must implement
the serde::Deserialize
trait.
You can derive serde::Deserialize
in most cases.
// [...]
#[derive(serde::Deserialize)]
pub struct HomeListing {
address: String,
price: u64,
}
Unsupported field types
UrlEncodedBody<T>
doesn't support deserializing nested structures.
For example, the following can't be deserialized from the wire using UrlEncodedBody<T>
:
use serde::Deserialize;
#[derive(Deserialize)]
pub struct UpdateUserBody {
address: Address
}
#[derive(Deserialize)]
pub struct Address {
street: String,
city: String,
}
If you need to deserialize nested structures from a URL encoded body,
you might want to look into writing your own extractor on top of a crate like
serde_qs
.
Avoiding allocations
If you want to minimize memory usage, you can try to avoid unnecessary heap memory allocations when deserializing string-like fields from the body of the incoming request. Pavex supports this use case—you can borrow from the request body.
Percent-encoding
It is not always possible to avoid allocations when handling a URL encoded body.
Fields and values in a URL encoded body must comply with the restriction of the URI specification:
you can only use a limited set of characters.
If you want to use a character that's not URL-safe, you
must percent-encode it.
For example, if you want to use a space in a field name or a field value, you must encode it as %20
.
A string like John Doe
becomes John%20Doe
when percent-encoded.
UrlEncodedBody<T>
automatically decodes percent-encoded strings for you. But that comes at a cost:
Pavex must allocate a new String
if the value is percent-encoded.
Cow
We recommend using Cow<'_, str>
as your field type for string-like parameters.
It borrows from the buffered request body if possible, it allocates a new String
if it can't be avoided.
Cow<'_, str>
strikes a balance between performance and robustness: you don't have to worry about a runtime
error if the field is percent-encoded, but you minimise memory usage when it is.