用 Rust 和 Tide 框架快速构建 CRUD API

Rust开发笔记
Rust开发笔记
发布于 2024-08-30 / 6 阅读
1
0

用 Rust 和 Tide 框架快速构建 CRUD API

在本文中,我们将使用 Rust 语言和 Tide 异步 Web 框架来构建一个基础的 CRUD API。我们将逐步讲解每个步骤,并提供完整的代码示例,帮助你快速上手 Rust Web 开发。

项目初始化

首先,创建一个新的 Rust 二进制项目:

cargo new tide-crud-api && cd tide-crud-api

接下来,添加所需的依赖项。我们将使用 tide 作为 Web 框架,serde 用于序列化和反序列化 JSON 数据,async-std 提供异步运行时支持。

# Cargo.toml

[dependencies]
tide = "0.17"
serde = { version = "1.0", features = ["derive"] }
async-std = { version = "1.12", features = ["attributes"] }

数据结构定义

我们将创建一个简单的 API 来管理书籍信息。首先,定义一个 Book 结构体来表示书籍数据:

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Book {
    id: u32,
    title: String,
    author: String,
}

我们使用 #[derive] 属性自动为 Book 结构体派生出 DebugCloneSerializeDeserialize trait 的实现,方便我们进行调试、复制、序列化和反序列化操作。

创建 Web 服务

接下来,创建我们的 Web 服务。我们将使用 tide::with_state 函数来创建一个带有共享状态的服务器,并在状态中存储一个 HashMap 来保存书籍数据。

use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use tide::{Body, Request, Response, Server};

#[derive(Clone)]
struct State {
    books: Arc<RwLock<HashMap<u32, Book>>>,
}

#[async_std::main]
async fn main() -> tide::Result<()> {
    // 初始化书籍数据
    let mut books: HashMap<u32, Book> = HashMap::new();
    books.insert(1, Book { id: 1, title: "The Rust Programming Language".to_string(), author: "Steve Klabnik and Carol Nichols".to_string() });
    books.insert(2, Book { id: 2, title: "Rust in Action".to_string(), author: "Tim McNamara".to_string() });

    // 创建共享状态
    let state = State {
        books: Arc::new(RwLock::new(books)),
    };

    // 创建 Tide 应用
    let mut app = server(state).await;

    // 启动服务器
    app.listen("127.0.0.1:8080").await?;

    Ok(())
}

async fn server(state: State) -> Server<State> {
    let mut app = tide::with_state(state);

    // 定义路由和处理函数
    app.at("/").get(index);
    app.at("/books").get(list_books).post(create_book);
    app.at("/books/:id").get(get_book).put(update_book).delete(delete_book);

    app
}

// 处理函数
async fn index(_: Request<State>) -> tide::Result {
    Ok("Welcome to the Book API!".into())
}

main 函数中,我们初始化了一个 HashMap 来存储书籍数据,并使用 ArcRwLock 将其包装成线程安全的共享状态。然后,我们创建了一个 Tide 服务器,并定义了路由和对应的处理函数。

实现 CRUD 操作

现在,让我们来实现具体的 CRUD 操作:

获取所有书籍

async fn list_books(req: Request<State>) -> tide::Result<Response> {
    let state = req.state();
    let books = state.books.read().unwrap();
    let books: Vec<Book> = books.values().cloned().collect();
    let mut res = Response::new(200);
    res.set_body(Body::from_json(&books)?);
    Ok(res)
}

这个函数首先获取共享状态中的书籍数据,然后将其转换为 Vec<Book> 并序列化成 JSON 格式,最后返回 HTTP 200 响应。

获取单个书籍

async fn get_book(req: Request<State>) -> tide::Result<Response> {
    let state = req.state();
    let id: u32 = req.param("id")?.parse()?;
    let books = state.books.read().unwrap();

    if let Some(book) = books.get(&id) {
        let mut res = Response::new(200);
        res.set_body(Body::from_json(book)?);
        Ok(res)
    } else {
        Ok(Response::new(404))
    }
}

这个函数首先从 URL 参数中获取书籍 ID,然后在共享状态中查找对应的书籍。如果找到,则返回 HTTP 200 响应,并将书籍数据序列化成 JSON 格式;否则,返回 HTTP 404 响应。

创建书籍

async fn create_book(mut req: Request<State>) -> tide::Result<Response> {
    let book: Book = req.body_json().await?;
    let mut state = req.state().books.write().unwrap();

    state.insert(book.id, book.clone());

    let mut res = Response::new(201);
    res.set_body(Body::from_json(&book)?);
    Ok(res)
}

这个函数首先从请求体中反序列化出 Book 结构体,然后将其插入到共享状态的书籍数据中,最后返回 HTTP 201 响应,并将新创建的书籍数据序列化成 JSON 格式。

更新书籍

async fn update_book(mut req: Request<State>) -> tide::Result<Response> {
    let id: u32 = req.param("id")?.parse()?;
    let updated_book: Book = req.body_json().await?;
    let mut state = req.state().books.write().unwrap();

    if let Some(book) = state.get_mut(&id) {
        *book = updated_book;
        let mut res = Response::new(200);
        res.set_body(Body::from_json(book)?);
        Ok(res)
    } else {
        Ok(Response::new(404))
    }
}

这个函数首先从 URL 参数中获取书籍 ID,然后从请求体中反序列化出更新后的 Book 结构体。接着,在共享状态中查找对应的书籍,如果找到,则更新书籍数据并返回 HTTP 200 响应;否则,返回 HTTP 404 响应。

删除书籍

async fn delete_book(req: Request<State>) -> tide::Result<Response> {
    let id: u32 = req.param("id")?.parse()?;
    let mut state = req.state().books.write().unwrap();

    if state.remove(&id).is_some() {
        Ok(Response::new(204))
    } else {
        Ok(Response::new(404))
    }
}

这个函数首先从 URL 参数中获取书籍 ID,然后在共享状态中查找对应的书籍。如果找到,则删除书籍数据并返回 HTTP 204 响应;否则,返回 HTTP 404 响应。

运行程序

完成以上代码后,运行程序:

cargo run

现在你可以使用 curl 或其他 HTTP 客户端工具来测试你的 API 了。

总结

本文介绍了如何使用 Rust 和 Tide 框架构建一个简易的 CRUD API。我们学习了如何定义数据结构、创建 Web 服务、实现 CRUD 操作以及运行程序。希望这篇文章能够帮助你入门 Rust Web 开发。


评论