pub struct FormRequest<T>(pub T);Expand description
Axum extractor that deserializes a URL-encoded form body into T and then sanitizes it.
T must implement both serde::de::DeserializeOwned and crate::sanitize::Sanitize.
Repeated form keys, nested structs, and Vec<Struct> rows all deserialize via serde_qs
form-encoding mode. Flat repeats (tag=a&tag=b) populate Vec<scalar> fields,
client[name]=… populates a nested struct, and indexed brackets (contacts[0][kind]=…)
populate Vec<Struct> rows. For per-row dynamic forms, the indexed form is required so
the deserializer can group fields into the correct row.
The body is read through axum::body::Bytes, so any
axum::extract::DefaultBodyLimit (or RequestBodyLimit middleware) applied to the
router is honored — oversized bodies short-circuit with 413 Payload Too Large
before deserialization runs.
§Errors
The FromRequest::Rejection is crate::Error. A 400 Bad Request is returned if
the body is not valid application/x-www-form-urlencoded data or cannot be deserialized
into T. If the body exceeds the configured limit, the inner Bytes extractor
surfaces a 413 Payload Too Large instead. The error renders via its
IntoResponse impl.
§Example
use modo::extractor::FormRequest;
use modo::sanitize::Sanitize;
use serde::Deserialize;
#[derive(Deserialize)]
struct Contact { kind: String, value: String, comment: String }
#[derive(Deserialize)]
struct NewClient {
name: String,
work_days: Vec<u8>, // multi-select checkbox group
contacts: Vec<Contact>, // contacts[0][kind]=…&contacts[0][value]=…
}
impl Sanitize for NewClient {
fn sanitize(&mut self) { self.name = self.name.trim().to_string(); }
}
async fn create(FormRequest(form): FormRequest<NewClient>) {
// form.contacts has one entry per submitted row
}Tuple Fields§
§0: T