Skip to content

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.

src/urlencoded/routes.rs
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:

src/blueprint.rs
use pavex::blueprint::Blueprint;
use pavex::request::body::UrlEncodedBody;

pub fn blueprint() -> Blueprint {
    let mut bp = Blueprint::new();
    UrlEncodedBody::register(&mut bp); // (1)!
    // [...]
}
  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:

src/urlencoded/routes.rs
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.

src/urlencoded/routes.rs
// [...]
#[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.