Skip to content

Установка и первые шаги

В этом разделе мы рассмотрим, как настроить проект с использованием Axum и создать первое работающее приложение. Мы пройдем через все шаги — от создания нового проекта до запуска веб-сервера и обработки HTTP-запросов.

Предварительные требования

Для работы с Axum вам потребуется:

  1. Rust и Cargo — актуальная версия Rust (желательно через rustup)
  2. Опыт с Rust — базовое понимание языка Rust (типы, трейты, владение, async/await)
  3. Базовые знания HTTP — понимание HTTP-методов, путей, заголовков и тела запроса

Установка и настройка проекта

Создание нового проекта

Начнем с создания нового проекта Rust с помощью Cargo:

bash
# Создание нового бинарного проекта
cargo new axum-demo
cd axum-demo

Добавление зависимостей

Отредактируйте файл Cargo.toml, добавив необходимые зависимости:

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 — сам фреймворк Axum
  • tokio — асинхронная среда выполнения
  • serde и serde_json — для сериализации/десериализации JSON
  • tower и tower-http — для middleware
  • tracing и tracing-subscriber — для логирования

Создание первого приложения

Теперь напишем простое приложение, которое отвечает на GET-запросы. Отредактируйте файл src/main.rs:

rust
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)
}

Объяснение кода

  1. Мы используем макрос #[tokio::main], который создает асинхронную среду выполнения Tokio.
  2. Создаем Router с двумя маршрутами:
    • / — отвечает статической строкой
    • /hello/:name — использует параметр из пути для приветствия
  3. Запускаем HTTP-сервер на локальном адресе 127.0.0.1:3000.
  4. Обработчики представлены асинхронными функциями, которые возвращают строки.

Запуск приложения

Запустите приложение с помощью Cargo:

bash
cargo run

Вы должны увидеть сообщение о запуске сервера:

INFO server_name::main: Сервер запущен на 127.0.0.1:3000

Тестирование API

Теперь вы можете протестировать ваше API с помощью браузера или инструмента командной строки, например curl:

bash
# Запрос к корневому маршруту
curl http://localhost:3000/

# Запрос к маршруту с параметром
curl http://localhost:3000/hello/Alice

Расширение приложения

Теперь добавим больше функциональности, включая обработку JSON:

rust
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:

bash
# Установка cargo-watch
cargo install cargo-watch

# Запуск с автоматической перезагрузкой при изменениях
cargo watch -x run

Дополнительные возможности

Axum предлагает множество других возможностей, которые будут рассмотрены в последующих разделах:

  • Middleware для аутентификации и авторизации
  • WebSockets для двунаправленной коммуникации
  • Обработка файлов и загрузка
  • Server-Sent Events (SSE)
  • Интеграция с базами данных
  • И многое другое

Полезные ресурсы

Заключение

В этом разделе мы познакомились с основами работы с Axum и создали простое, но функциональное API. Мы рассмотрели базовую установку проекта, определение маршрутов, создание обработчиков и запуск веб-сервера.

В следующих разделах мы углубимся в более продвинутые темы, такие как маршрутизация, экстракторы, middleware и многое другое.