Что такое Axum и зачем он нужен?
Axum — это современный, эргономичный и производительный веб-фреймворк для языка Rust, разработанный командой Tokio. Он построен на основе популярных крейтов tokio, tower и hyper, что обеспечивает высокую производительность и асинхронность "из коробки".
Основная цель Axum — предоставить разработчикам интуитивно понятный API для создания веб-приложений и API, сохраняя при этом всю мощь и безопасность Rust. В отличие от более "низкоуровневых" решений, Axum абстрагирует сложность работы с HTTP-протоколом, позволяя сосредоточиться на бизнес-логике.
Axum идеально подходит для:
- Создания RESTful API и микросервисов
- Разработки веб-приложений с серверным рендерингом
- Построения прокси-серверов и middleware
- Реализации WebSocket-соединений
- Любых сценариев, где требуется высокая производительность и надежность
Установка Axum
Поскольку Rust — это компилируемый язык, установка происходит через менеджер пакетов Cargo. Добавьте следующие зависимости в ваш файл Cargo.toml:
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.5", features = ["cors", "trace"] }
tracing = "0.1"
tracing-subscriber = "0.3"
После этого выполните команду cargo build, чтобы скачать и скомпилировать зависимости.
Быстрый старт: первое приложение на Axum
Создайте файл src/main.rs и напишите простейший HTTP-сервер:
use axum::{
routing::get,
Router,
};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// Создаем маршрутизатор
let app = Router::new()
.route("/", get(root_handler))
.route("/hello/:name", get(hello_handler));
// Запускаем сервер
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Сервер запущен на {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn root_handler() -> &'static str {
"Добро пожаловать на главную страницу!"
}
async fn hello_handler(axum::extract::Path(name): axum::extract::Path) -> String {
format!("Привет, {}!", name)
}
Запустите сервер командой cargo run и откройте браузер по адресу http://localhost:3000/hello/Мир.
Основные методы и функции Axum
1. Router::new()
Описание: Создает новый пустой маршрутизатор. Это основная точка входа для конфигурации вашего приложения.
Параметры: Нет.
Пример:
use axum::Router;
let app: Router = Router::new();
2. Router::route()
Описание: Добавляет маршрут к указанному пути с заданным HTTP-методом и обработчиком.
Параметры:
path: &str— путь маршрута (может содержать динамические сегменты, например/users/:id)method_router: MethodRouter— результат вызоваget(),post(),put(),delete()и т.д.
Пример:
use axum::{routing::get, Router};
async fn users_list() -> &'static str { "Список пользователей" }
async fn create_user() -> &'static str { "Создание пользователя" }
let app = Router::new()
.route("/users", get(users_list).post(create_user));
3. routing::get(), post(), put(), delete()
Описание: Создают обработчик для соответствующего HTTP-метода.
Параметры:
handler: Handler— асинхронная функция, которая обрабатывает запрос
Пример:
use axum::routing::{get, post, put, delete};
async fn get_user() -> &'static str { "GET" }
async fn update_user() -> &'static str { "PUT" }
async fn delete_user() -> &'static str { "DELETE" }
let app = Router::new()
.route("/users/:id", get(get_user).put(update_user).delete(delete_user));
4. axum::extract::Path
Описание: Извлекает параметры пути из URL. Поддерживает как одиночные значения, так и структуры.
Параметры: Generic параметр, определяющий тип извлекаемого значения.
Пример:
use axum::extract::Path;
use serde::Deserialize;
#[derive(Deserialize)]
struct UserParams {
id: u32,
action: String,
}
async fn user_action(Path(params): Path) -> String {
format!("Действие {} для пользователя {}", params.action, params.id)
}
// URL: /users/42/edit
// Результат: "Действие edit для пользователя 42"
5. axum::extract::Query
Описание: Извлекает параметры запроса (query string) из URL.
Параметры: Generic параметр, определяющий структуру с полями, соответствующими параметрам запроса.
Пример:
use axum::extract::Query;
use serde::Deserialize;
#[derive(Deserialize)]
struct SearchParams {
q: String,
page: Option,
}
async fn search(Query(params): Query) -> String {
let page = params.page.unwrap_or(1);
format!("Поиск '{}', страница {}", params.q, page)
}
// URL: /search?q=rust&page=2
// Результат: "Поиск 'rust', страница 2"
6. axum::extract::Json
Описание: Извлекает JSON-тело запроса и десериализует его в указанный тип.
Параметры: Generic параметр, определяющий тип данных для десериализации.
Пример:
use axum::extract::Json;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Serialize)]
struct UserResponse {
id: u32,
name: String,
}
async fn create_user(Json(payload): Json) -> Json {
// Логика создания пользователя
Json(UserResponse {
id: 1,
name: payload.name,
})
}
7. axum::response::Json
Описание: Создает JSON-ответ с автоматической сериализацией данных.
Параметры: Generic параметр, реализующий Serialize.
Пример:
use axum::response::Json;
use serde::Serialize;
#[derive(Serialize)]
struct ApiResponse {
success: bool,
message: String,
}
async fn api_endpoint() -> Json {
Json(ApiResponse {
success: true,
message: "Операция выполнена успешно".to_string(),
})
}
8. axum::http::StatusCode
Описание: Позволяет возвращать HTTP-статус коды вместе с ответом.
Параметры: Константы из модуля axum::http::StatusCode.
Пример:
use axum::{http::StatusCode, response::IntoResponse};
async fn not_found() -> impl IntoResponse {
(StatusCode::NOT_FOUND, "Ресурс не найден")
}
async fn created() -> impl IntoResponse {
(StatusCode::CREATED, "Ресурс создан")
}
9. axum::middleware::from_fn
Описание: Создает middleware из пользовательской функции для обработки запросов до или после основного обработчика.
Параметры:
middleware_fn: fn— асинхронная функция, принимающаяRequestиNext
Пример:
use axum::{
middleware,
routing::get,
Router,
http::Request,
response::Response,
};
async fn auth_middleware(
req: Request,
next: middleware::Next,
) -> Response {
// Проверка авторизации
if let Some(auth_header) = req.headers().get("Authorization") {
// Продолжаем обработку
next.run(req).await
} else {
// Возвращаем 401
(axum::http::StatusCode::UNAUTHORIZED, "Требуется авторизация").into_response()
}
}
let app = Router::new()
.route("/protected", get(protected_handler))
.layer(middleware::from_fn(auth_middleware));
10. Router::with_state()
Описание: Позволяет передавать общее состояние (state) во все обработчики маршрутов.
Параметры:
state: S— произвольное значение, которое будет доступно в обработчиках
Пример:
use axum::{extract::State, routing::get, Router};
use std::sync::Arc;
struct AppState {
db_pool: String,
}
async fn get_users(State(state): State>) -> String {
format!("Подключение к БД: {}", state.db_pool)
}
let state = Arc::new(AppState {
db_pool: "postgres://localhost/mydb".to_string(),
});
let app = Router::new()
.route("/users", get(get_users))
.with_state(state);
Реальный практический пример: REST API для управления задачами
Создадим полноценное REST API для управления списком задач (Todo List) с использованием Axum:
use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::Json,
routing::{delete, get, post, put},
Router,
};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Task {
id: String,
title: String,
completed: bool,
}
#[derive(Debug, Deserialize)]
struct CreateTask {
title: String,
}
#[derive(Debug, Deserialize)]
struct UpdateTask {
title: Option,
completed: Option,
}
type Db = Arc>>;
#[tokio::main]
async fn main() {
let db: Db = Arc::new(Mutex::new(HashMap::new()));
let app = Router::new()
.route("/tasks", get(list_tasks).post(create_task))
.route("/tasks/:id", get(get_task).put(update_task).delete(delete_task))
.with_state(db);
let addr = "0.0.0.0:3000".parse().unwrap();
println!("Сервер запущен на {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn list_tasks(State(db): State) -> Json> {
let tasks = db.lock().unwrap();
Json(tasks.values().cloned().collect())
}
async fn create_task(
State(db): State,
Json(payload): Json,
) -> (StatusCode, Json) {
let task = Task {
id: Uuid::new_v4().to_string Также в библиотеке
Работа с Excel в C++: библиотеки, примеры и инструменты
Мониторинг приложений C++: инструменты и библиотеки
WebSocket библиотеки Rust: Tokio-tungstenite и async-tungstenite для реального времени
Валидация данных в Swift (iOS/macOS): лучшие практики и инструменты
gRPC в Rust: библиотеки Tonic и Grpc-rs с примерами