Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Extractors

Extractors are the mechanism handlers use to pull data out of incoming requests. Volter provides two traits:

  • FromRequestParts — extract from request metadata (URI, headers, extensions) without consuming the body. Runs first, enables early rejection.
  • FromRequest — extract from the full request, including the body. Runs after all FromRequestParts extractors.

How Extraction Works

When you write a handler with multiple parameters, Volter runs them in order:

#![allow(unused)]
fn main() {
async fn handler(
    // 1. FromRequestParts — runs first
    Query(query): Query<PageQuery>,
    // 2. FromRequestParts — runs second
    State(state): State<AppState>,
    // 3. FromRequest — runs last (may consume body)
    Json(body): Json<CreateUser>,
) -> impl IntoResponse {
    // All extractors have already succeeded by this point
}
}

The last parameter may implement either trait. All earlier parameters must implement FromRequestParts. This means metadata extractors run before the body is consumed, allowing fast rejection of invalid requests.

Available Extractors

ExtractorTraitSourceRejection
Query<T>FromRequestPartsURL query stringQueryRejection → 400
Path<T>FromRequestPartsURL path parametersPathRejection → 400
Extension<T>FromRequestPartsRequest extensionsExtensionRejection → 500
State<T>FromRequestPartsApplication stateNever fails
Json<T>FromRequestJSON bodyJsonRejection → 400/415/500

Mapping Rejections to Responses

Every extractor defines its own rejection type. Each rejection implements IntoResponse, so you can compose handlers freely:

#![allow(unused)]
fn main() {
// This handler may fail with 400 (invalid query) or 400 (invalid JSON body).
// Volter short-circuits: if Query fails, Json is never extracted.
async fn create(Query(q): Query<SearchParams>, Json(body): Json<CreateUser>) -> impl IntoResponse {
    // ...
}
}

The FromRequestParts Trait

#![allow(unused)]
fn main() {
pub trait FromRequestParts<S>: Sized {
    type Rejection: IntoResponse;
    type Future: Future<Output = Result<Self, Self::Rejection>> + Send;
    fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Self::Future;
}
}

The FromRequest Trait

#![allow(unused)]
fn main() {
pub trait FromRequest<S, B = BoxBody>: Sized {
    type Rejection: IntoResponse;
    type Future: Future<Output = Result<Self, Self::Rejection>> + Send;
    fn from_request(req: http::Request<B>, state: &S) -> Self::Future;
}
}

Every FromRequestParts implementor also implements FromRequest (the body is split off and discarded).