Axum Routing & Handlers
Defina rotas HTTP, vincule handlers a métodos e retorne respostas tipadas com Axum 0.8.
Receita
Cartão de referência rápida - pronto para copiar e colar.
use axum::{routing::{get, post, delete}, Router, Json};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)] struct CreateUser { email: String }
#[derive(Serialize)] struct User { id: u64, email: String }
async fn create(Json(body): Json<CreateUser>) -> Json<User> {
Json(User { id: 1, email: body.email })
}
let app = Router::new()
.route("/users", get(list_users).post(create))
.route("/users/:id", get(get_user).delete(delete_user));Quando usar isso: Você precisa de roteamento explícito de métodos HTTP com corpos de solicitação e resposta tipados.
Exemplo de Trabalho
use axum::{
Router, extract::{Path, State},
http::StatusCode,
routing::{delete, get, post},
Json,
};
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize)]
struct Item { id: u64, name: String }
#[derive(Deserialize)]
struct NewItem { name: String }
type Store = std::sync::Arc<tokio::sync::RwLock<Vec<Item>>>;
async fn list(State(store): State<Store>) -> Json<Vec<Item>> {
Json(store.read().await.clone())
}
async fn create(
State(store): State<Store>,
Json(body): Json<NewItem>,
) -> (StatusCode, Json<Item>) {
let mut guard = store.write().await;
let item = Item { id: guard.len() as u64 + 1, name: body.name };
guard.push(item.clone());
(StatusCode::CREATED, Json(item))
}
async fn remove(Path(id): Path<u64>, State(store): State<Store>) -> StatusCode {
let mut guard = store.write().await;
if let Some(pos) = guard.iter().position(|i| i.id == id) {
guard.remove(pos);
StatusCode::NO_CONTENT
} else {
StatusCode::NOT_FOUND
}
}
#[tokio::main]
async fn main() {
let store = Store::default();
let app = Router::new()
.route("/items", get(list).post(create))
.route("/items/:id", delete(remove))
.with_state(store);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}O que isso demonstra:
- Rotas estilo REST com handlers específicos de método no mesmo caminho.
- Tuplas de código de status como
(StatusCode::CREATED, Json(item))para respostas precisas. - Extratores
PatheStatecombinados em uma única assinatura de handler. - Armazenamento em memória
RwLockpara uma demonstração executável sem um banco de dados.
Mergulho Profundo
Como Funciona
- Um
Routeré umtower::Serviceque corresponde a requisições por caminho e método. - Handlers são funções assíncronas cujos últimos parâmetros são extratores; Axum executa extratores em paralelo quando possível.
- Tipos de retorno implementam
IntoResponse- strings, tuplas,Json,StatusCode, ou tipos customizados.
Padrões de Rota
| Padrão | Exemplo | Notas |
|---|---|---|
| Estático | /health | Correspondência exata |
| Parâmetro | /users/:id | Capturado como Path<T> |
| Wildcard | /files/*path | Segmentos restantes como um parâmetro |
| Fallback | .fallback(handler) | 404 ou shell SPA |
Notas de Rust
use axum::response::Redirect;
async fn old() -> Redirect { Redirect::permanent("/v2/old") }Armadilhas
- Conflitos de ordem de rota - Rotas estáticas e de parâmetro sobrepostas podem corresponder inesperadamente. Correção: Registre caminhos estáticos como
/users/meantes de/users/:id. - Estado ausente - Handlers que usam
Statesem.with_state()entram em pânico em tempo de execução. Correção: Sempre chame.with_state()no roteador final antes de servir. - Bloqueio em handlers assíncronos - I/O pesado na CPU ou bloqueante paralisa o worker do Tokio. Correção: Use
tokio::task::spawn_blockingou drivers de banco de dados assíncronos. - Método errado retorna 405 - Clientes POST para uma rota somente GET. Correção: Documente os métodos permitidos; retorne o cabeçalho
Allowem handlers 405 customizados, se necessário. - Incompatibilidade de campo Serde - Chaves JSON não correspondem aos campos da struct. Correção: Adicione
#[serde(rename = "camelCase")]ou retorne um mapeamento explícito 422.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Actix-web | A equipe prefere o modelo de ator ou um ecossistema maduro | Você quer a composabilidade do ecossistema Tower |
| Rocket | Macros procedurais e roteamento em tempo de compilação atraem | Você precisa de mágica mínima e traits assíncronos estáveis |
| hyper diretamente | Construindo uma pilha HTTP customizada | APIs REST padrão com roteamento e extratores |
FAQs
Como adiciono um handler 404?
Use .fallback(handler) no roteador.
Handlers podem ser síncronos?
Sim, com async fn envolvendo código síncrono via spawn_blocking, mas prefira I/O assíncrono.
Como versionar APIs?
Aninhe sob /v1 com Router::nest.
Múltiplos roteadores?
Mescle com Router::merge ou aninhe sob prefixos.
Documentação OpenAPI?
Adicione utoipa ou aide - não é embutido no Axum.
Terminação HTTPS?
Geralmente no balanceador de carga; Axum serve HTTP atrás de um proxy TLS.
Path vs Query para filtros?
Path para identidade de recurso; query para filtros opcionais e paginação.
Cabeçalhos de resposta customizados?
Retorne (StatusCode, [(HeaderName, &str)], Json(body)).
Downloads de arquivos?
Use axum::body::Body ou tower_http::services::ServeDir.
Testando rotas?
Use tower::ServiceExt::oneshot sem vincular uma porta.
CORS?
Adicione tower_http::cors::CorsLayer.
Limites de taxa?
Aplique tower_governor ou uma camada Tower customizada.
Relacionado
- Noções Básicas de Backends Web - Primeira configuração de servidor
- Extratores & Estado - Estado compartilhado e extratores
- Tratamento de Erros em Handlers - Erros tipados
- Middleware & Tower - Camadas transversais
- Testando Serviços Web - Testes de rota
Versões da Stack: 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+.