Typed Extractors

Parse request bodies, query strings, and headers into typed values using FromRequest.

FromRequest trait

FromRequest is defined in src/extract/mod.rs:

pub trait FromRequest: Sized {
    fn from_request(request: &Request) -> Result<Self, Response>;
}

Call T::from_request(request) to extract a typed value from the incoming request. On failure the implementation returns a ready-to-send Response (usually 400 Bad Request) so handlers can early-return without duplicating error-building code.

Built-in extractors

Body — raw bytes

Clones the raw request body into a Vec<u8>. Never fails; an empty body produces an empty Vec.

use rust_web_server::extract::{Body, FromRequest};

fn handler(request: &Request) -> Response {
    let Body(bytes) = Body::from_request(request).unwrap();
    // bytes is Vec<u8>
    todo!()
}

Body also exposes .into_bytes(self) -> Vec<u8> for consuming the wrapper.

BodyText — UTF-8 decoded body

Returns 400 Bad Request if the body bytes are not valid UTF-8.

use rust_web_server::extract::{BodyText, FromRequest};
use rust_web_server::error::{AppError, IntoResponse};

fn handler(request: &Request) -> Response {
    let text = match BodyText::from_request(request) {
        Ok(BodyText(s)) => s,
        Err(err_response) => return err_response,
    };
    // text is String
    todo!()
}

BodyText exposes .as_str(&self) -> &str for borrowing.

Query — parsed query string

Parses the query string from request.request_uri into a HashMap<String, String>. Never fails; a URI with no query string produces an empty map.

use rust_web_server::extract::{Query, FromRequest};

fn handler(request: &Request) -> Response {
    let q = Query::from_request(request).unwrap();
    let page = q.get("page").map(String::as_str).unwrap_or("1");
    let limit = q.get("limit").map(String::as_str).unwrap_or("20");
    // ...
    todo!()
}

Query exposes .get(&self, key: &str) -> Option<&String>.

RequestHeaders — all request headers

Clones the entire header list. Never fails.

use rust_web_server::extract::{RequestHeaders, FromRequest};

fn handler(request: &Request) -> Response {
    let headers = RequestHeaders::from_request(request).unwrap();
    let auth = headers.get("Authorization"); // case-insensitive lookup
    // ...
    todo!()
}

RequestHeaders::get(&self, name: &str) -> Option<&str> performs a case-insensitive lookup and returns the value of the first matching header.

RequestId — correlation ID

The value of the X-Request-Id header (set by RequestIdLayer). Never fails; .0 is an empty string when the header is absent.

use rust_web_server::extract::{RequestId, FromRequest};

let id = RequestId::from_request(request).unwrap();
let id: &str = id.as_str();

Two more extractors live outside extract: json::Json<T> (serde feature, see JSON) and validate::Validated<T> (see Validation).

Combining extractors

Extractors are ordinary function calls; combine as many as you need:

use rust_web_server::extract::{BodyText, Query, RequestHeaders, FromRequest};

fn create_item(request: &Request, _params: &PathParams, _conn: &ConnectionInfo, _state: &()) -> Response {
    // Early-return on bad body
    let BodyText(body) = match BodyText::from_request(request) {
        Ok(b) => b,
        Err(r) => return r,
    };

    let query = Query::from_request(request).unwrap();
    let headers = RequestHeaders::from_request(request).unwrap();

    let dry_run = query.get("dry_run").map(|v| v == "true").unwrap_or(false);
    let trace_id = headers.get("X-Trace-Id").unwrap_or("none");

    // process body, dry_run, trace_id ...
    todo!()
}

Writing a custom extractor

Implement FromRequest for any type that can be derived from a Request:

use rust_web_server::extract::FromRequest;
use rust_web_server::request::Request;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::header::Header;
use rust_web_server::range::Range;
use rust_web_server::mime_type::MimeType;

/// Extracts a Bearer token from the Authorization header.
pub struct BearerToken(pub String);

impl FromRequest for BearerToken {
    fn from_request(request: &Request) -> Result<Self, Response> {
        let auth = request
            .get_header("Authorization".to_string())
            .and_then(|h| h.value.strip_prefix("Bearer ").map(str::to_string));

        match auth {
            Some(token) => Ok(BearerToken(token)),
            None => {
                let header_list = Header::get_header_list(request);
                let body = Range::get_content_range(
                    b"Missing or invalid Authorization header".to_vec(),
                    MimeType::TEXT_PLAIN.to_string(),
                );
                Err(Response::get_response(
                    STATUS_CODE_REASON_PHRASE.n401_unauthorized,
                    Some(header_list),
                    Some(vec![body]),
                ))
            }
        }
    }
}

// Usage:
// let BearerToken(token) = BearerToken::from_request(request)?;

#[derive(FromRequest)]

The macros feature can generate the boilerplate above for a named-field struct where every field is itself already an extractor — each field's from_request runs in declaration order, and the first failure short-circuits with that field's error response:

# Cargo.toml
rust-web-server = { version = "18", features = ["macros"] }
use rust_web_server::extract::{BodyText, Query};

#[derive(rust_web_server::FromRequest)]
struct Payload {
    body: BodyText,
    params: Query,
}

// let Payload { body, params } = Payload::from_request(request)?;

This covers structs built from existing extractors (Body, BodyText, Query, RequestHeaders, RequestId, or your own custom ones). A field like BearerToken above works too, as long as it implements FromRequest itself — the derive doesn't know how to pull an arbitrary field from a query parameter or header on its own; write a small custom extractor (as shown above) for that and include it as a field.

Edit this page on GitHub