Установка и первые шаги
В этом разделе мы рассмотрим, как настроить проект с использованием Axum и создать первое работающее приложение. Мы пройдем через все шаги — от создания нового проекта до запуска веб-сервера и обработки HTTP-запросов.
Предварительные требования
Для работы с Axum вам потребуется:
- Rust и Cargo — актуальная версия Rust (желательно через rustup)
- Опыт с Rust — базовое понимание языка Rust (типы, трейты, владение,
async/await
) - Базовые знания HTTP — понимание HTTP-методов, путей, заголовков и тела запроса
Установка и настройка проекта
Создание нового проекта
Начнем с создания нового проекта Rust с помощью Cargo:
# Создание нового бинарного проекта
cargo new axum-demo
cd axum-demo
Добавление зависимостей
Отредактируйте файл Cargo.toml
, добавив необходимые зависимости:
[package]
name = "axum-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7.2"
tokio = { version = "1.34.0", features = ["full"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
tower = "0.4.13"
tower-http = { version = "0.5.0", features = ["trace"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
Этот набор зависимостей включает:
axum
— сам фреймворк Axumtokio
— асинхронная среда выполненияserde
иserde_json
— для сериализации/десериализации JSONtower
иtower-http
— для middlewaretracing
иtracing-subscriber
— для логирования
Создание первого приложения
Теперь напишем простое приложение, которое отвечает на GET-запросы. Отредактируйте файл src/main.rs
:
use axum::{
routing::get,
Router,
};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// Инициализация трассировки для логирования
tracing_subscriber::fmt::init();
// Создание роутера с одним маршрутом
let app = Router::new()
.route("/", get(root))
.route("/hello/:name", get(hello_name));
// Определение адреса для запуска сервера
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::info!("Сервер запущен на {}", addr);
// Запуск сервера
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// Обработчик для корневого маршрута
async fn root() -> &'static str {
"Привет, Axum!"
}
// Обработчик с параметром пути
async fn hello_name(axum::extract::Path(name): axum::extract::Path<String>) -> String {
format!("Привет, {}!", name)
}
Объяснение кода
- Мы используем макрос
#[tokio::main]
, который создает асинхронную среду выполнения Tokio. - Создаем
Router
с двумя маршрутами:/
— отвечает статической строкой/hello/:name
— использует параметр из пути для приветствия
- Запускаем HTTP-сервер на локальном адресе
127.0.0.1:3000
. - Обработчики представлены асинхронными функциями, которые возвращают строки.
Запуск приложения
Запустите приложение с помощью Cargo:
cargo run
Вы должны увидеть сообщение о запуске сервера:
INFO server_name::main: Сервер запущен на 127.0.0.1:3000
Тестирование API
Теперь вы можете протестировать ваше API с помощью браузера или инструмента командной строки, например curl
:
# Запрос к корневому маршруту
curl http://localhost:3000/
# Запрос к маршруту с параметром
curl http://localhost:3000/hello/Alice
Расширение приложения
Теперь добавим больше функциональности, включая обработку JSON:
use axum::{
routing::{get, post},
http::StatusCode,
response::IntoResponse,
Json, Router,
extract::Path,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
// Структура для пользователя
#[derive(Debug, Serialize, Clone)]
struct User {
id: u64,
name: String,
email: String,
}
// Структура для создания пользователя
#[derive(Debug, Deserialize)]
struct CreateUser {
name: String,
email: String,
}
// Состояние приложения
#[derive(Clone)]
struct AppState {
users: Arc<Mutex<HashMap<u64, User>>>,
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
// Создание состояния приложения
let state = AppState {
users: Arc::new(Mutex::new(HashMap::new())),
};
// Создание роутера
let app = Router::new()
.route("/", get(root))
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(get_user))
.with_state(state);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::info!("Сервер запущен на {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// Обработчики
async fn root() -> &'static str {
"API пользователей"
}
async fn list_users(
axum::extract::State(state): axum::extract::State<AppState>
) -> impl IntoResponse {
let users = state.users.lock().unwrap();
let users_vec: Vec<User> = users.values().cloned().collect();
Json(users_vec)
}
async fn create_user(
axum::extract::State(state): axum::extract::State<AppState>,
Json(payload): Json<CreateUser>
) -> impl IntoResponse {
let mut users = state.users.lock().unwrap();
// Генерация ID (в реальном приложении лучше использовать UUID)
let id = users.len() as u64 + 1;
let user = User {
id,
name: payload.name,
email: payload.email,
};
users.insert(id, user.clone());
(StatusCode::CREATED, Json(user))
}
async fn get_user(
Path(id): Path<u64>,
axum::extract::State(state): axum::extract::State<AppState>
) -> impl IntoResponse {
let users = state.users.lock().unwrap();
match users.get(&id) {
Some(user) => Ok(Json(user.clone())),
None => Err(StatusCode::NOT_FOUND),
}
}
Структура проекта для больших приложений
По мере роста вашего приложения рекомендуется организовать код в модули. Типичная структура проекта на Axum может выглядеть так:
src/
├── main.rs # Точка входа, настройка и запуск сервера
├── routes/ # Определения маршрутов и обработчиков
│ ├── mod.rs
│ ├── users.rs
│ └── auth.rs
├── models/ # Структуры данных и логика домена
│ ├── mod.rs
│ └── user.rs
├── services/ # Бизнес-логика
│ ├── mod.rs
│ └── user_service.rs
├── middleware/ # Пользовательские middleware
│ ├── mod.rs
│ └── auth.rs
└── error.rs # Обработка ошибок
Горячая перезагрузка
Для повышения продуктивности разработки рекомендуется использовать инструмент для горячей перезагрузки, такой как cargo-watch
:
# Установка cargo-watch
cargo install cargo-watch
# Запуск с автоматической перезагрузкой при изменениях
cargo watch -x run
Дополнительные возможности
Axum предлагает множество других возможностей, которые будут рассмотрены в последующих разделах:
- Middleware для аутентификации и авторизации
- WebSockets для двунаправленной коммуникации
- Обработка файлов и загрузка
- Server-Sent Events (SSE)
- Интеграция с базами данных
- И многое другое
Полезные ресурсы
Заключение
В этом разделе мы познакомились с основами работы с Axum и создали простое, но функциональное API. Мы рассмотрели базовую установку проекта, определение маршрутов, создание обработчиков и запуск веб-сервера.
В следующих разделах мы углубимся в более продвинутые темы, такие как маршрутизация, экстракторы, middleware и многое другое.