Rocket для Rust: полное руководство с примерами
Что это и зачем нужна?
Rocket — это современный веб-фреймворк для языка Rust, который ставит во главу угла удобство разработки, безопасность и производительность. В отличие от многих других веб-фреймворков, Rocket использует мощную систему типов Rust для проверки корректности кода на этапе компиляции, что позволяет избежать целого класса runtime-ошибок. Он предоставляет декларативный API для маршрутизации, встроенную поддержку сериализации/десериализации через Serde, автоматическую валидацию данных из форм и URL, а также систему middleware на основе трейтов.
Зачем нужен Rocket? Если вы пишете на Rust и хотите создать веб-приложение (API, сайт, микросервис) с минимальным количеством шаблонного кода и максимальной безопасностью — Rocket идеальный выбор. Он особенно хорош для проектов, где важна надёжность: финансовые сервисы, системы управления данными, backend для мобильных приложений. Благодаря асинхронной архитектуре на основе Tokio, Rocket легко справляется с тысячами одновременных соединений.
Основные преимущества Rocket: типобезопасные маршруты (компилятор проверяет, что вы не передадите строку туда, где ожидается число), автоматическая генерация документации через Rocket.toml, встроенная поддержка WebSocket, простая работа с базами данных через Diesel или SQLx, и нулевая стоимость абстракций — все проверки выполняются на этапе компиляции, без замедления runtime.
Установка
Для работы с Rocket вам понадобится установленный Rust (рекомендуется версия 1.75+). Установка фреймворка производится через менеджер пакетов Cargo. Добавьте зависимость в файл Cargo.toml вашего проекта:
[package]
name = "my_rocket_app"
version = "0.1.0"
edition = "2021"
[dependencies]
rocket = "0.5.1"
После этого выполните команду для загрузки зависимостей:
cargo build
Для работы с JSON вам потребуется добавить Serde:
rocket = { version = "0.5.1", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
Установка завершена. Теперь вы можете создавать веб-приложения на Rust с помощью Rocket.
Быстрый старт — минимальный рабочий пример
Создайте файл src/main.rs со следующим содержимым:
#[macro_use] extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Привет, мир Rocket!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])
}
Запустите приложение командой cargo run. После компиляции откройте браузер и перейдите по адресу http://localhost:8000. Вы увидите сообщение "Привет, мир Rocket!". Поздравляю, ваше первое Rocket-приложение работает!
Разберём код: #[get("/")] — атрибут, указывающий, что функция обрабатывает GET-запросы к корневому пути. #[launch] — макрос, создающий точку входа. rocket::build() создаёт экземпляр приложения, а .mount() привязывает маршруты к базовому пути.
Основные методы/функции/классы
1. #[get(path)] — Обработка GET-запросов
Сигнатура: #[get("/path")] fn handler() -> T
Что делает: Атрибут, который регистрирует функцию как обработчик HTTP GET-запросов по указанному пути. Возвращаемое значение автоматически сериализуется в HTTP-ответ.
#[get("/hello")]
fn hello() -> &'static str {
"Hello, Rocket!"
}
#[get("/user/<id>")]
fn user(id: u32) -> String {
format!("User ID: {}", id)
}
2. #[post(path)] — Обработка POST-запросов
Сигнатура: #[post("/path", data = "<variable>")] fn handler(variable: DataType) -> T
Что делает: Атрибут для обработки POST-запросов. Параметр data указывает, какие данные ожидать в теле запроса. Rocket автоматически десериализует JSON, формы или другие форматы.
use rocket::serde::{Deserialize, json::Json};
#[derive(Deserialize)]
struct User {
name: String,
age: u8,
}
#[post("/user", data = "<user>")]
fn create_user(user: Json<User>) -> String {
format!("Created user: {} ({} years old)", user.name, user.age)
}
3. rocket::build() — Создание экземпляра приложения
Сигнатура: pub fn build() -> Rocket<Build>
Что делает: Создаёт новый экземпляр Rocket-приложения в состоянии сборки. Позволяет настраивать маршруты, middleware, состояние и другие компоненты перед запуском.
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/api", routes![hello, create_user])
.attach(Fairing::from(|rocket| {
println!("Server starting...");
rocket
}))
}
4. .mount(base, routes) — Привязка маршрутов
Сигнатура: pub fn mount(self, base: &str, routes: Vec<Route>) -> Self
Что делает: Привязывает список маршрутов к указанному базовому пути. Все маршруты будут доступны по адресу /base/path. Позволяет группировать эндпоинты по логическим разделам.
#[get("/status")]
fn status() -> &'static str { "OK" }
#[get("/version")]
fn version() -> &'static str { "1.0.0" }
fn configure_app() -> rocket::Rocket<rocket::Build> {
rocket::build()
.mount("/health", routes![status, version])
.mount("/api/v1", routes![create_user, get_user])
}
5. State<T> — Управление состоянием приложения
Сигнатура: pub struct State<'r, T: Send + Sync + 'static>(&'r T)
Что делает: Позволяет хранить и получать доступ к глобальному состоянию приложения (например, пул соединений с БД, конфигурация). Состояние должно быть потокобезопасным (Send + Sync).
use std::sync::atomic::{AtomicUsize, Ordering};
struct AppState {
counter: AtomicUsize,
}
#[get("/count")]
fn count(state: &State<AppState>) -> String {
let current = state.counter.fetch_add(1, Ordering::SeqCst);
format!("Request count: {}", current + 1)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.manage(AppState { counter: AtomicUsize::new(0) })
.mount("/", routes![count])
}
6. Form<T> — Обработка HTML-форм
Сигнатура: pub struct Form<T>(pub T)
Что делает: Автоматически десериализует данные из URL-encoded формы (application/x-www-form-urlencoded) в структуру Rust. Поддерживает вложенные структуры и валидацию.
use rocket::form::Form;
#[derive(FromForm)]
struct LoginForm {
username: String,
password: String,
remember_me: Option<bool>,
}
#[post("/login", data = "<form>")]
fn login(form: Form<LoginForm>) -> String {
format!(
"Login attempt: username={}, remember={}",
form.username,
form.remember_me.unwrap_or(false)
)
}
7. #[catch(status)] — Обработка ошибок
Сигнатура: #[catch(status_code)] fn handler() -> T
Что делает: Регистрирует функцию-обработчик для определённого HTTP-статуса ошибки (404, 500, 422 и т.д.). Позволяет возвращать кастомные страницы ошибок или JSON-ответы.
#[catch(404)]
fn not_found() -> &'static str {
"Страница не найдена. Проверьте URL."
}
#[catch(500)]
fn internal_error() -> rocket::response::content::Json<&'static str> {
rocket::response::content::Json("{\"error\":\"Внутренняя ошибка сервера\"}")
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![index])
.register("/", catchers![not_found, internal_error])
}
8. Fairing — Middleware-система
Сигнатура: pub trait Fairing: Send + Sync + 'static { ... }
Что делает: Позволяет внедрять кастомную логику на различных этапах обработки запроса (до, после, при запуске/остановке). Используется для логирования, аутентификации, CORS и других сквозных задач.
use rocket::{Rocket, Build, fairing::{Fairing, Info, Kind}};
struct RequestLogger;
#[rocket::async_trait]
impl Fairing for RequestLogger {
fn info(&self) -> Info {
Info {
name: "Request Logger",
kind: Kind::Request | Kind::Response
}
}
async fn on_request(&self, request: &mut rocket::Request<'_>, _: &mut rocket::Data<'_>) {
println!("→ {} {}", request.method(), request.uri());
}
async fn on_response<'r>(&self, request: &'r rocket::Request<'_>, response: &mut rocket::Response<'r>) {
println!("← {} {}", response.status(), request.uri());
}
}
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(RequestLogger)
.mount("/", routes![index])
}
9. rocket::response::content::Json — JSON-ответы
Сигнатура: pub struct Json<T>(pub T)
Что делает: Оборачивает данные в JSON-ответ с правильным Content-Type. Работает с любыми типами, реализующими Serialize из Serde. Автоматически устанавливает заголовки.
use rocket::serde::{Serialize, json::Json};
#[derive(Serialize)]
struct ApiResponse {
success: bool,
message: String,
data: Option<Vec<String>>,
}
#[get("/api/data")]
fn get_data() -> Json<ApiResponse> {
Json(ApiResponse {
success: true,
message: "Данные получены".to_string(),
data: Some(vec!["item1".into(), "item2".into()]),
})
}
10. rocket::tokio::spawn — Асинхронные задачи
Сигнатура: pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
Что делает: Запускает асинхронную задачу в фоновом режиме. Используется для выполнения длительных операций (отправка email, обработка файлов) без блокировки основного потока обработки запросов.
use rocket::tokio;
#[get("/process")]
async fn process() -> &'static str {
tokio::spawn(async {
// Имитация длительной операции
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
println!("Фоновая задача завершена");
});
"Задача запущена в фоне"
}
Реальный практический пример (100-200 строк кода)
Создадим полноценное REST API для управления списком задач (Todo List) с хранением в памяти. Этот пример демонстрирует: CRUD-операции, состояние приложения, обработку ошибок, JSON-сериализацию и асинхронные операции.
#[macro_use] extern crate rocket;
use rocket::serde::{Deserialize, Serialize, json::Json};
use rocket::tokio::sync::Mutex;
use std::collections::HashMap;
use std::sync::Arc;
// Типы данных
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Todo {
id: u64,
title: String,
completed: bool,
}
#[derive(Debug, Deserialize)]
struct CreateTodoRequest {
title: String,
}
#[derive(Debug, Deserialize)]
struct UpdateTodoRequest {
title: Option<String>,
completed: Option<bool>,
}
// Состояние приложения
type TodoStore = Arc<Mutex<HashMap<u64, Todo>>>;
struct AppState {
store: TodoStore,
next_id: Mutex<u64>,
}
// Обработчики марш