RESTful API #

本节将通过一个完整的示例,演示如何使用Rocket构建RESTful API。

项目结构 #

text
api_project/
├── Cargo.toml
├── Rocket.toml
├── src/
│   ├── main.rs
│   ├── models/
│   │   ├── mod.rs
│   │   └── user.rs
│   ├── routes/
│   │   ├── mod.rs
│   │   └── user.rs
│   ├── db/
│   │   ├── mod.rs
│   │   └── pool.rs
│   └── error.rs
└── migrations/

依赖配置 #

toml
[package]
name = "rocket_api"
version = "0.1.0"
edition = "2021"

[dependencies]
rocket = { version = "0.5", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }

模型定义 #

rust
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use chrono::{DateTime, Utc};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    pub id: Uuid,
    pub username: String,
    pub email: String,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Deserialize)]
pub struct CreateUser {
    pub username: String,
    pub email: String,
    pub password: String,
}

#[derive(Debug, Deserialize)]
pub struct UpdateUser {
    pub username: Option<String>,
    pub email: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct UserResponse {
    pub id: Uuid,
    pub username: String,
    pub email: String,
    pub created_at: DateTime<Utc>,
}

impl From<User> for UserResponse {
    fn from(user: User) -> Self {
        Self {
            id: user.id,
            username: user.username,
            email: user.email,
            created_at: user.created_at,
        }
    }
}

错误处理 #

rust
use rocket::http::Status;
use rocket::response::{Responder, Result};
use rocket::serde::json::Json;
use serde::Serialize;

#[derive(Debug, Serialize)]
pub struct ErrorResponse {
    pub error: String,
    pub code: u16,
}

#[derive(Debug)]
pub enum ApiError {
    NotFound(String),
    BadRequest(String),
    Conflict(String),
    Internal(String),
}

impl<'r> Responder<'r, 'r> for ApiError {
    fn respond_to(self, _req: &'r rocket::Request<'_>) -> Result<'r> {
        let (status, message) = match self {
            ApiError::NotFound(m) => (Status::NotFound, m),
            ApiError::BadRequest(m) => (Status::BadRequest, m),
            ApiError::Conflict(m) => (Status::Conflict, m),
            ApiError::Internal(m) => (Status::InternalServerError, m),
        };
        
        rocket::response::Response::build()
            .status(status)
            .header(rocket::http::ContentType::JSON)
            .sized_body(
                serde_json::to_string(&ErrorResponse {
                    error: message,
                    code: status.code,
                }).unwrap().len(),
                std::io::Cursor::new(
                    serde_json::to_string(&ErrorResponse {
                        error: String::new(),
                        code: status.code,
                    }).unwrap()
                )
            )
            .ok()
    }
}

数据库模拟 #

rust
use std::collections::HashMap;
use std::sync::Mutex;
use uuid::Uuid;
use chrono::Utc;

pub struct Db {
    users: Mutex<HashMap<Uuid, crate::models::User>>,
}

impl Db {
    pub fn new() -> Self {
        Self {
            users: Mutex::new(HashMap::new()),
        }
    }
    
    pub fn create(&self, user: crate::models::CreateUser) -> crate::models::User {
        let mut users = self.users.lock().unwrap();
        let now = Utc::now();
        let new_user = crate::models::User {
            id: Uuid::new_v4(),
            username: user.username,
            email: user.email,
            created_at: now,
            updated_at: now,
        };
        users.insert(new_user.id, new_user.clone());
        new_user
    }
    
    pub fn find(&self, id: Uuid) -> Option<crate::models::User> {
        self.users.lock().unwrap().get(&id).cloned()
    }
    
    pub fn find_all(&self) -> Vec<crate::models::User> {
        self.users.lock().unwrap().values().cloned().collect()
    }
    
    pub fn update(&self, id: Uuid, update: crate::models::UpdateUser) -> Option<crate::models::User> {
        let mut users = self.users.lock().unwrap();
        if let Some(user) = users.get_mut(&id) {
            if let Some(username) = update.username {
                user.username = username;
            }
            if let Some(email) = update.email {
                user.email = email;
            }
            user.updated_at = Utc::now();
            return Some(user.clone());
        }
        None
    }
    
    pub fn delete(&self, id: Uuid) -> bool {
        self.users.lock().unwrap().remove(&id).is_some()
    }
}

路由实现 #

rust
use rocket::serde::json::Json;
use rocket::State;
use rocket::http::Status;
use crate::models::{CreateUser, UpdateUser, UserResponse};
use crate::db::Db;
use crate::error::ApiError;

#[get("/users")]
pub fn list(db: &State<Db>) -> Json<Vec<UserResponse>> {
    Json(db.find_all().into_iter().map(UserResponse::from).collect())
}

#[get("/users/<id>")]
pub fn get(id: uuid::Uuid, db: &State<Db>) -> Result<Json<UserResponse>, ApiError> {
    db.find(id)
        .map(UserResponse::from)
        .map(Json)
        .ok_or_else(|| ApiError::NotFound(format!("User {} not found", id)))
}

#[post("/users", format = "json", data = "<user>")]
pub fn create(user: Json<CreateUser>, db: &State<Db>) -> (Status, Json<UserResponse>) {
    let created = db.create(user.into_inner());
    (Status::Created, Json(UserResponse::from(created)))
}

#[put("/users/<id>", format = "json", data = "<user>")]
pub fn update(
    id: uuid::Uuid,
    user: Json<UpdateUser>,
    db: &State<Db>,
) -> Result<Json<UserResponse>, ApiError> {
    db.update(id, user.into_inner())
        .map(UserResponse::from)
        .map(Json)
        .ok_or_else(|| ApiError::NotFound(format!("User {} not found", id)))
}

#[delete("/users/<id>")]
pub fn delete(id: uuid::Uuid, db: &State<Db>) -> Result<Status, ApiError> {
    if db.delete(id) {
        Ok(Status::NoContent)
    } else {
        Err(ApiError::NotFound(format!("User {} not found", id)))
    }
}

主程序 #

rust
#[macro_use] extern crate rocket;

mod models;
mod routes;
mod db;
mod error;

use db::Db;

#[get("/")]
fn index() -> &'static str {
    "API v1.0"
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .manage(Db::new())
        .mount("/", routes![index])
        .mount("/api/v1", routes![
            routes::user::list,
            routes::user::get,
            routes::user::create,
            routes::user::update,
            routes::user::delete
        ])
}

API测试 #

bash
# 创建用户
curl -X POST http://localhost:8000/api/v1/users \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","email":"alice@example.com","password":"secret"}'

# 获取用户列表
curl http://localhost:8000/api/v1/users

# 获取单个用户
curl http://localhost:8000/api/v1/users/{id}

# 更新用户
curl -X PUT http://localhost:8000/api/v1/users/{id} \
  -H "Content-Type: application/json" \
  -d '{"username":"alice_updated"}'

# 删除用户
curl -X DELETE http://localhost:8000/api/v1/users/{id}

下一步 #

掌握了RESTful API后,让我们继续学习 用户认证系统,实现完整的用户认证功能。

最后更新:2026-03-28