Введение: Почему Rust для REST API?
Rust — это современный системный язык программирования, который сочетает в себе производительность C/C++ и безопасность памяти. В последние годы он активно используется для создания высоконагруженных веб-сервисов и REST API. Благодаря фреймворку Actix-web, написать полноценное REST API на Rust можно буквально за 30 минут, даже если вы только начинаете изучать язык.
В этой статье мы создадим простое, но рабочее REST API для управления списком задач (Todo List). Вы научитесь:
- Настраивать проект на Rust с Actix-web
- Реализовывать CRUD-операции (Create, Read, Update, Delete)
- Работать с JSON-запросами и ответами
- Использовать маршрутизацию и обработчики
Готовы? Поехали!
Шаг 1: Настройка проекта и установка зависимостей
Для начала убедитесь, что у вас установлен Rust. Если нет — выполните команду:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Создайте новый проект:
cargo new rust_todo_api
cd rust_todo_api
Откройте Cargo.toml и добавьте следующие зависимости:
[dependencies]
actix-web = "4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
Здесь:
- actix-web — мощный асинхронный веб-фреймворк
- serde — для сериализации/десериализации JSON
- tokio — асинхронный рантайм (Actix-web использует его под капотом)
Выполните cargo build, чтобы скачать и скомпилировать зависимости. Это займёт 2-3 минуты.
Шаг 2: Создание модели данных и хранилища
В этом проекте мы не будем подключать базу данных — для простоты используем вектор в памяти. Создадим структуру для задачи.
Откройте src/main.rs и напишите следующий код:
use actix_web::{web, App, HttpServer, HttpResponse, middleware};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
// Модель задачи
#[derive(Serialize, Deserialize, Clone)]
struct Task {
id: u32,
title: String,
completed: bool,
}
// Хранилище задач (потокобезопасное)
struct AppState {
tasks: Mutex<Vec<Task>>,
}
Пояснения:
- #[derive(Serialize, Deserialize)] — автоматически генерирует методы для работы с JSON
- Mutex — обеспечивает безопасный доступ к данным из нескольких потоков
- AppState — будет храниться в состоянии приложения
Шаг 3: Реализация CRUD-обработчиков
Теперь добавим функции-обработчики для каждого HTTP-метода. Вставьте следующий код после объявления структур:
// Получить все задачи
async fn get_tasks(data: web::Data<AppState>) -> HttpResponse {
let tasks = data.tasks.lock().unwrap();
HttpResponse::Ok().json(tasks.clone())
}
// Создать новую задачу
async fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> HttpResponse {
let mut tasks = data.tasks.lock().unwrap();
let new_id = tasks.len() as u32 + 1;
let task = Task {
id: new_id,
title: body.title.clone(),
completed: body.completed,
};
tasks.push(task.clone());
HttpResponse::Created().json(task)
}
// Обновить задачу
async fn update_task(
data: web::Data<AppState>,
path: web::Path<u32>,
body: web::Json<Task>,
) -> HttpResponse {
let mut tasks = data.tasks.lock().unwrap();
let id = path.into_inner();
if let Some(task) = tasks.iter_mut().find(|t| t.id == id) {
task.title = body.title.clone();
task.completed = body.completed;
HttpResponse::Ok().json(task.clone())
} else {
HttpResponse::NotFound().body("Task not found")
}
}
// Удалить задачу
async fn delete_task(data: web::Data<AppState>, path: web::Path<u32>) -> HttpResponse {
let mut tasks = data.tasks.lock().unwrap();
let id = path.into_inner();
let initial_len = tasks.len();
tasks.retain(|t| t.id != id);
if tasks.len() < initial_len {
HttpResponse::Ok().body("Task deleted")
} else {
HttpResponse::NotFound().body("Task not found")
}
}
Каждый обработчик принимает web::Data<AppState> — это способ Actix-web передавать общее состояние. Обратите внимание на использование web::Path для извлечения ID из URL.
Шаг 4: Настройка маршрутизации и запуск сервера
Осталось собрать всё вместе и запустить сервер. Добавьте функцию main:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let app_state = web::Data::new(AppState {
tasks: Mutex::new(vec![
Task {
id: 1,
title: "Изучить Rust".to_string(),
completed: false,
},
Task {
id: 2,
title: "Написать REST API".to_string(),
completed: true,
},
]),
});
println!("Сервер запущен на http://127.0.0.1:8080");
HttpServer::new(move || {
App::new()
.app_data(app_state.clone())
.route("/tasks", web::get().to(get_tasks))
.route("/tasks", web::post().to(create_task))
.route("/tasks/{id}", web::put().to(update_task))
.route("/tasks/{id}", web::delete().to(delete_task))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Запустите сервер командой cargo run. Если всё сделано правильно, вы увидите сообщение "Сервер запущен на http://127.0.0.1:8080".
Тестирование API
Теперь протестируем наше API с помощью curl или Postman:
-
Блоги
Рекомендаций по книгам
Читайте также