ESC
Type to search...

Extractors

Parse request data with type safety

Extractors automatically parse request data and inject it into your handlers. If parsing fails, they return appropriate error responses.

Available Extractors

ExtractorDescription
Path<T>URL path parameters
Query<T>Query string parameters
Json<T>JSON request body
Form<T>URL-encoded form data
HeadersRequest headers
State<T>Application state
ContextRequest context (trace_id)
Cookie<T>Typed cookie access
CurrentUserAuthenticated user (JWT)
Validated<T>Validated extractor
PaginatePagination params (requires feature)
DbDatabase connection (requires feature)

Path Parameters

Extract values from URL path segments:

#[get("/users/:id")]
async fn get_user(id: Path<u64>) -> String {
    format!("User ID: {}", id.into_inner())
}

#[get("/posts/:year/:month")]
async fn archive(year: Path<u32>, month: Path<u32>) -> String {
    format!("{}/{}", year.into_inner(), month.into_inner())
}

Query Parameters

Parse query strings into typed structs:

#[derive(Deserialize)]
struct Pagination {
    page: Option<u32>,
    limit: Option<u32>,
}

#[get("/users")]
async fn list_users(query: Query<Pagination>) -> String {
    let page = query.0.page.unwrap_or(1);
    let limit = query.0.limit.unwrap_or(20);
    format!("Page {} with {} items", page, limit)
}

JSON Body

Parse JSON request bodies:

#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[post("/users")]
async fn create_user(body: Json<CreateUser>) -> Json<User> {
    let input = body.into_inner();
    // Create user...
    Json(user)
}

Form Data

Parse URL-encoded form submissions:

#[derive(Deserialize)]
struct LoginForm {
    username: String,
    password: String,
}

#[post("/login")]
async fn login(form: Form<LoginForm>) -> Result<Json<TokenResponse>> {
    let credentials = form.into_inner();
    // Authenticate...
}

Headers

Access request headers:

#[get("/debug")]
async fn debug(headers: Headers) -> String {
    let user_agent = headers
        .get("user-agent")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("unknown");

    format!("User-Agent: {}", user_agent)
}

Application State

Access shared application state:

#[derive(Clone)]
struct AppConfig {
    app_name: String,
}

#[get("/info")]
async fn info(config: State<AppConfig>) -> String {
    format!("App: {}", config.into_inner().app_name)
}

Cookies

Deserialize cookies into typed structs:

#[derive(Deserialize)]
struct Session {
    session_id: String,
}

#[get("/dashboard")]
async fn dashboard(session: Cookie<Session>) -> String {
    format!("Session: {}", session.into_inner().session_id)
}

Returns 400 Bad Request if required cookies are missing or malformed.

Request Context

Access the request context with trace ID:

#[get("/trace")]
async fn trace(ctx: Context) -> String {
    format!("Trace ID: {}", ctx.trace_id())
}

Validation

Validate extracted data using the validator crate:

use validator::Validate;

#[derive(Deserialize, Validate)]
struct CreateUser {
    #[validate(email)]
    email: String,

    #[validate(length(min = 8))]
    password: String,
}

#[post("/users")]
async fn create_user(body: Validated<Json<CreateUser>>) -> Json<User> {
    // body is guaranteed to be valid
    let input = body.into_inner().into_inner();
    // ...
}

If validation fails, returns 422 with validation error details.

Multiple Extractors

You can use multiple extractors in a single handler:

#[post("/users/:id/posts")]
async fn create_post(
    id: Path<u64>,
    user: CurrentUser,
    body: Json<CreatePost>,
) -> Result<Json<Post>> {
    // All extractors available
}

Note: Only one body-consuming extractor (Json, Form) can be used per handler.