Actix-web для Rust: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Actix-web и зачем он нужен?

Actix-web — это мощный, прагматичный и чрезвычайно быстрый веб-фреймворк для языка программирования Rust. Он построен на основе акторной модели (библиотека Actix) и асинхронного рантайма Tokio. Actix-web позволяет создавать высокопроизводительные веб-приложения и API, способные обрабатывать миллионы запросов в секунду.

Зачем использовать Actix-web?

  • Производительность: Actix-web стабильно входит в тройку самых быстрых веб-фреймворков в мире (согласно тестам TechEmpower), опережая такие решения, как Go net/http и Node.js Express.
  • Безопасность: Rust гарантирует безопасность памяти на этапе компиляции, исключая целые классы уязвимостей (переполнение буфера, гонки данных).
  • Типобезопасность: Благодаря строгой системе типов Rust, многие ошибки (например, неверный формат JSON) отлавливаются на этапе компиляции.
  • Асинхронность: Полная поддержка async/await позволяет обрабатывать тысячи одновременных соединений без создания потоков на каждое.
  • Экосистема: Actix-web легко интегрируется с другими крейтами Rust: serde (сериализация), sqlx/diesel (базы данных), reqwest (HTTP-клиент).

Actix-web идеально подходит для: REST API, микросервисов, WebSocket-серверов, высоконагруженных веб-приложений, прокси-серверов.

Установка Actix-web

Так как Actix-web — это библиотека для Rust, установка производится через менеджер пакетов cargo (аналог pip для Python).

Шаг 1. Убедитесь, что у вас установлен Rust. Проверьте командой:

rustc --version
cargo --version

Если Rust не установлен, скачайте его с rustup.rs.

Шаг 2. Создайте новый проект:

cargo new my_actix_app
cd my_actix_app

Шаг 3. Добавьте Actix-web в зависимости. Откройте файл Cargo.toml и добавьте:

[dependencies]
actix-web = "4"

Шаг 4. Установите зависимости и соберите проект:

cargo build

Готово! Теперь вы можете использовать Actix-web в своем проекте.

Быстрый старт: "Hello, World!" на Actix-web

Создадим простейший веб-сервер, который отвечает на GET-запросы.

Пример 1: Минимальный сервер

// main.rs
use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello, World!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(hello)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Запуск: cargo run

Проверка: Откройте браузер по адресу http://127.0.0.1:8080 или выполните:

curl http://127.0.0.1:8080

Вы увидите ответ: Hello, World!

Основные методы и функции Actix-web

Рассмотрим ключевые элементы фреймворка. Для каждого приведено описание, параметры и рабочий пример.

1. #[get("/path")] — макрос для обработки GET-запросов

Что делает: Декорирует асинхронную функцию, превращая её в обработчик HTTP GET-запросов по указанному пути.

Параметры: Путь (строка), может содержать динамические сегменты (например, /{id}).

Пример:

use actix_web::{get, HttpResponse, Responder};

#[get("/user/{id}")]
async fn get_user(path: actix_web::web::Path<String>) -> impl Responder {
    let user_id = path.into_inner();
    HttpResponse::Ok().body(format!("User ID: {}", user_id))
}

2. #[post("/path")] — макрос для обработки POST-запросов

Что делает: Аналогично #[get], но для POST-запросов. Используется для создания ресурсов.

Параметры: Путь (строка).

Пример:

use actix_web::{post, HttpResponse, Responder};

#[post("/user")]
async fn create_user() -> impl Responder {
    HttpResponse::Created().body("User created")
}

3. HttpResponse::Ok() — создание HTTP-ответа 200 OK

Что делает: Создает ответ с кодом состояния 200. Позволяет добавлять тело, заголовки, куки.

Параметры: Нет обязательных. Методы: .body(), .json(), .insert_header().

Пример:

use actix_web::{HttpResponse, Responder};

async fn index() -> impl Responder {
    HttpResponse::Ok()
        .insert_header(("X-Custom-Header", "value"))
        .body("Success!")
}

4. web::Json<T> — извлечение JSON из тела запроса

Что делает: Автоматически десериализует тело запроса в структуру Rust. Требует, чтобы тип T реализовывал serde::Deserialize.

Параметры: Тип данных (структура).

Пример:

use actix_web::{post, web, HttpResponse, Responder};
use serde::Deserialize;

#[derive(Deserialize)]
struct UserInput {
    name: String,
    age: u8,
}

#[post("/submit")]
async fn submit(form: web::Json<UserInput>) -> impl Responder {
    HttpResponse::Ok().json(format!("Hello, {}! You are {} years old.", form.name, form.age))
}

5. web::Query<T> — извлечение query-параметров

Что делает: Парсит параметры строки запроса (например, ?name=John&age=30) в структуру.

Параметры: Тип данных с Deserialize.

Пример:

use actix_web::{get, web, HttpResponse, Responder};
use serde::Deserialize;

#[derive(Deserialize)]
struct SearchParams {
    q: String,
    page: Option<u32>,
}

#[get("/search")]
async fn search(params: web::Query<SearchParams>) -> impl Responder {
    let page = params.page.unwrap_or(1);
    HttpResponse::Ok().body(format!("Search for '{}' on page {}", params.q, page))
}

6. web::Path<T> — извлечение параметров пути

Что делает: Извлекает динамические сегменты из URL-пути (например, /users/42 → 42).

Параметры: Тип данных (обычно String, u32 или кортеж).

Пример:

use actix_web::{get, web, HttpResponse, Responder};

#[get("/product/{id}/review/{review_id}")]
async fn get_review(path: web::Path<(u32, u32)>) -> impl Responder {
    let (product_id, review_id) = path.into_inner();
    HttpResponse::Ok().body(format!("Review {} for product {}", review_id, product_id))
}

7. App::new().service() — регистрация обработчиков

Что делает: Добавляет обработчик (функцию с макросом) в приложение. Без этого сервер не будет знать о ваших эндпоинтах.

Параметры: Функция-обработчик, задекорированная макросом (#[get], #[post] и т.д.).

Пример:

use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn root() -> impl Responder {
    HttpResponse::Ok().body("Root")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(root) // регистрация
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

8. HttpServer::new() — создание HTTP-сервера

Что делает: Создает экземпляр сервера. Принимает замыкание, которое возвращает App.

Параметры: Замыкание || -> App.

Пример:

HttpServer::new(|| {
    App::new()
        .route("/", web::get().to(|| async { HttpResponse::Ok().body("Hello") }))
})
.bind("127.0.0.1:8080")?
.run()
.await

9. .bind() — привязка к адресу и порту

Что делает: Указывает, на каком IP-адресе и порту слушать входящие соединения.

Параметры: Кортеж (IpAddr, u16) или строка "адрес:порт".

Пример:

server.bind(("0.0.0.0", 3000))?; // слушать на всех интерфейсах, порт 3000

10. web::Data<T> — разделяемое состояние приложения

Что делает: Позволяет передавать данные (например, пул соединений к БД, конфигурацию) во все обработчики. Данные создаются один раз и доступны для чтения.

Параметры: Тип данных, которые нужно разделить.

Пример:

use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use std::sync::Mutex;

struct AppState {
    counter: Mutex<u32>,
}

#[get("/count")]
async fn count(data: web::Data<AppState>) -> impl Responder {
    let mut c = data.counter.lock().unwrap();
    *c += 1;
    HttpResponse::Ok().body(format!("Counter: {}", c))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let state = web::Data::new(AppState {
        counter: Mutex::new(0),
    });

    HttpServer::new(move || {
        App::new()
            .app_data(state.clone())
            .service(count)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Реальный практический пример: REST API для заметок

Создадим полноценное REST API для управления заметками (Notes). Будем хранить данные в памяти (для простоты).

Структура проекта:

Рекомендаций