引言:为什么用Rust开发REST API?
Rust是一种现代系统编程语言,兼具C++的性能和内存安全性。近年来,它已成为构建高负载Web服务的热门选择。如果你已经熟悉Python并想尝试新事物,Rust将是一个绝佳的进阶选择。
在本文中,我们将使用Actix-web框架,用Rust创建一个功能完整的任务管理(Todo List)REST API。按照指南操作,整个过程不超过30分钟。
1. 环境准备
安装Rust
如果你尚未安装Rust,请在终端(Linux/macOS)中执行以下命令:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh对于Windows,请从官方网站下载安装程序。
安装后检查版本:
rustc --version创建项目
使用Cargo(Rust的包管理器)创建一个新项目:
cargo new rust_todo_apicd rust_todo_api添加依赖
打开Cargo.toml文件,添加以下依赖:
[dependencies]actix-web = "4"actix-rt = "2"serde = { version = "1", features = ["derive"] }serde_json = "1"tokio = { version = "1", features = ["full"] }保存文件。首次构建时,Cargo会自动下载所有库。
2. 创建数据模型和存储
在编写端点之前,先定义数据结构。我们将使用内存中的简单向量(用于演示)。
任务模型
创建文件src/models.rs:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]pub struct Task { pub id: u32, pub title: String, pub completed: bool,}全局存储
在src/main.rs中添加全局状态。为了线程安全,使用Mutex:
use std::sync::Mutex;use actix_web::{web, App, HttpServer, HttpResponse, HttpRequest, Responder};use models::Task;
mod models;
struct AppState { tasks: Mutex<Vec<Task>>, next_id: Mutex<u32>,}3. 实现CRUD端点
现在编写主要的处理函数。它们都将通过web::Data接收应用状态。
GET /tasks — 获取所有任务
async fn get_tasks(data: web::Data<AppState>) -> impl Responder { let tasks = data.tasks.lock().unwrap(); HttpResponse::Ok().json(tasks.clone())}POST /tasks — 创建新任务
async fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder { let mut tasks = data.tasks.lock().unwrap(); let mut next_id = data.next_id.lock().unwrap(); let new_task = Task { id: *next_id, title: body.title.clone(), completed: false, }; tasks.push(new_task.clone()); *next_id += 1; HttpResponse::Created().json(new_task)}GET /tasks/{id} — 根据ID获取任务
async fn get_task(data: web::Data<AppState>, path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); let tasks = data.tasks.lock().unwrap(); match tasks.iter().find(|t| t.id == id) { Some(task) => HttpResponse::Ok().json(task), None => HttpResponse::NotFound().json("Task not found"), }}DELETE /tasks/{id} — 删除任务
async fn delete_task(data: web::Data<AppState>, path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); l