Extractors & State
Handlers do Axum recebem dados da requisição através de extractors - State, Json, Path, Query, headers, e mais.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use axum::extract::{Path, Query, State};
use axum::Json;
use serde::Deserialize;
#[derive(Clone)]
struct AppState { pool: sqlx::PgPool }
#[derive(Deserialize)]
struct Search { q: Option<String> }
async fn handler(
State(state): State<AppState>,
Path(id): Path<u64>,
Query(search): Query<Search>,
Json(body): Json<CreateDto>,
) -> Result<Json<Item>, AppError> {
// use state.pool, id, search.q, body
}Quando usar isso: Todo handler do Axum que lê dados de path, query, body ou dados compartilhados da aplicação.
Exemplo de Trabalho
use axum::{
extract::{Path, Query, State},
routing::get,
Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
items: Arc<tokio::sync::RwLock<Vec<String>>>,
}
#[derive(Deserialize)]
struct Filter { prefix: Option<String> }
#[derive(Serialize)]
struct ItemView { index: usize, name: String }
async fn list(
State(state): State<AppState>,
Query(filter): Query<Filter>,
) -> Json<Vec<ItemView>> {
let items = state.items.read().await;
let views: Vec<_> = items
.iter()
.enumerate()
.filter(|(_, name)| {
filter.prefix.as_ref().map(|p| name.starts_with(p)).unwrap_or(true)
})
.map(|(i, name)| ItemView { index: i, name: name.clone() })
.collect();
Json(views)
}
async fn get_one(Path(index): Path<usize>, State(state): State<AppState>) -> Json<ItemView> {
let items = state.items.read().await;
let name = items.get(index).cloned().unwrap_or_default();
Json(ItemView { index, name })
}
#[tokio::main]
async fn main() {
let state = AppState {
items: Arc::new(tokio::sync::RwLock::new(vec!["alpha".into(), "beta".into()])),
};
let app = Router::new()
.route("/items", get(list))
.route("/items/:index", get(get_one))
.with_state(state);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}O que isso demonstra:
Statecompartilha dados embrulhados emArcentre handlers.Querypermite filtragem opcional sem novas rotas.Pathcaptura identificadores de recursos da URL.- Múltiplos extractors se compõem em uma única assinatura de handler.
Mergulho Profundo
Como Funciona
- Extractors implementam
FromRequestPartsouFromRequeste rodam antes do corpo do handler. State<T>clonaTpor requisição - useArcpara pools, clientes e configurações.- Falha na extração mapeia para erros HTTP (400 para path/query inválido, 422 para JSON).
Extractors Comuns
| Extractor | Fonte | Uso Típico |
|---|---|---|
Path<T> | Segmentos da URL | IDs de Recursos |
Query<T> | String de Query | Filtros, paginação |
Json<T> | Corpo da Requisição | Payloads POST/PATCH |
State<T> | Estado do Router | Pool de BD, configuração |
HeaderMap | Headers | Tokens de autenticação, tracing |
Notas de Rust
// Extractor opcional - o parâmetro inteiro pode estar ausente
use axum::extract::OptionalQuery;
async fn maybe(Query(q): OptionalQuery<Filter>) -> String { /* ... */ }Armadilhas
- State não é Clone -
AppStatecom campos não-Clone falha na compilação. Correção: Embrulhe emArcouArc<Mutex<_>>. - Ordem dos Extractors - Extractors de
Bodyconsomem o corpo; apenas um extractor de body por handler. Correção: Use um únicoJsonou um extractor customizadoFromRequest. - State em routers aninhados - Sub-routers herdam o state do
.with_state()do pai. Correção: Chame.with_state()uma vez no router de nível superior. - Rigidez na desserialização de Query - Campos desconhecidos falham por padrão. Correção: Adicione
#[serde(deny_unknown_fields)]intencionalmente ou permita extras. - Incompatibilidade de tipo em Path -
/users/:idcomPath<String>quando clientes enviam números. Correção: Escolha o tipo correto ou valide manualmente.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Extensões de Requisição (Request extensions) | Metadados por requisição de middleware | Configuração compartilhada para toda a aplicação |
Extension<T> (padrão depreciado) | Apps Tower legados | Novos projetos Axum 0.8 - prefira State |
Parsing manual de Request | Controle total sobre streaming do corpo | APIs JSON padrão |
FAQs
State vs Extension?
Axum 0.8 usa State para dados da aplicação; evite Extension depreciado para código novo.
Múltiplos tipos de state?
Combine em uma única struct AppState - Axum suporta um tipo de state por router.
Transação de BD por requisição?
Abra no handler a partir do pool em State, ou use middleware que insere uma extensão de transação.
Extractor de autenticação?
Implemente FromRequestParts para AuthUser lendo o header de Autorização.
Uploads multipart?
Use axum::extract::Multipart para campos de arquivo.
Bytes brutos do corpo?
Use o extractor Bytes de axum::body.
Validar parâmetros de query?
Use o crate validator na struct Query ou verificações manuais no handler.
State aninhado em testes?
Construa o router com .with_state(test_state) em cada módulo de teste.
Extrair cookies?
Use axum_extra::extract::CookieJar do crate axum-extra.
Customizar rejeições (rejections)?
Implemente IntoResponse em tipos de rejeição customizados para extractors.
Relacionados
- Noções Básicas de Backends Web - Exemplos introdutórios
- Roteamento e Handlers do Axum - Definições de rotas
- Validação de Requisições - Restrições de campos
- Autenticação e Sessões - Extractors de autenticação
- Tratamento de Erros em Handlers - Erros de extractors
Versões das Pilhas: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.