Rust:warp 编写 RESTful API 的示例及原理分析

9 篇文章 0 订阅

1. 源代码

不多说,直接贴代码:

Cargo.toml

[package]
name = "restfultest"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
warp = "0.3"  
tokio = { version = "1", features = ["full"] }
crossbeam = "0.8.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = { version = "0.4", features = ["serde"] }
rand = "0.8.5"
lazy_static = "1.4"

main.rs

use lazy_static::lazy_static;
use std::collections::HashMap;
use warp::Filter;
use std::sync::Mutex;


// 模拟数据库
lazy_static! {
    static ref DB: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
}

#[tokio::main]
async fn main() {
    // 定义路由
    let routes = warp::get()
        .and(warp::path!("items" / String))
        .and_then(get_item)
        .or(warp::post()
            .and(warp::path("items"))
            .and(warp::body::json())
            .and_then(create_item))
        .or(warp::put()
            .and(warp::path!("items" / String))
            .and(warp::body::json())
            .and_then(update_item))
        .or(warp::delete()
            .and(warp::path!("items" / String))
            .and_then(delete_item));

    // 启动服务器
    warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
}

// 处理 GET 请求,获取指定项目
async fn get_item(id: String) -> Result<impl warp::Reply, warp::Rejection> {
    let db = DB.lock().unwrap();
    match db.get(&id) {
        Some(item) => Ok(warp::reply::json(&item)),
        None => Err(warp::reject::not_found()),
    }
}

// 处理 POST 请求,创建新项目
async fn create_item(item: HashMap<String, String>) -> Result<impl warp::Reply, warp::Rejection> {
    let mut db = DB.lock().unwrap();
    let id = item.get("id").unwrap_or(&String::new()).to_string();
    db.insert(id, item.get("value").unwrap_or(&String::new()).to_string());
    Ok(warp::reply::json(&item))
}

// 处理 PUT 请求,更新指定项目
async fn update_item(
    id: String,
    item: HashMap<String, String>,
) -> Result<impl warp::Reply, warp::Rejection> {
    let mut db = DB.lock().unwrap();
    if db.contains_key(&id) {
        db.insert(id, item.get("value").unwrap_or(&String::new()).to_string());
        Ok(warp::reply::json(&item))
    } else {
        Err(warp::reject::not_found())
    }
}

// 处理 DELETE 请求,删除指定项目
async fn delete_item(id: String) -> Result<impl warp::Reply, warp::Rejection> {
    let mut db = DB.lock().unwrap();
    if db.remove(&id).is_some() {
        Ok(warp::reply())
    } else {
        Err(warp::reject::not_found())
    }
}

2. 过滤器的工作原理

在Warp框架中,routes变量是通过组合多个过滤器来定义的,这些过滤器共同描述了如何匹配和处理HTTP请求。下面详细解释routes是如何接收参数,并把参数传递给子过滤器的,以及andor运算在Warp过滤器中的作用。

参数传递

Warp的过滤器之间传递参数主要是通过组合和链式调用的方式。当一个请求到达Warp服务器时,它会按照定义的过滤器链进行匹配和处理。在这个过程中,每个过滤器都可以访问和修改请求数据,也可以产生一些中间结果,这些结果可以被链中的后续过滤器使用。

例如,在warp::path!("items" / String)中,String是一个捕获参数,当请求的路径匹配items/<some_string>模式时,<some_string>部分会被捕获并作为一个String类型的值传递给后续的过滤器或处理函数。

and 运算

在Warp中,and运算用于串联过滤器,确保请求必须依次通过所有串联的过滤器。每个and后面的过滤器都会接收到前面过滤器产生的结果,并且可以基于这些结果进行进一步的处理或决策。

例如,在warp::get().and(warp::path!("items" / String))中,warp::get()过滤器首先检查请求方法是否为GET,如果是,则继续将请求传递给warp::path!("items" / String)过滤器进行路径匹配。如果路径也匹配成功,那么捕获到的路径参数会传递给后续的.and_then(get_item)处理函数。

or 运算

or运算在Warp中用于提供备选的路由处理逻辑。当or前面的过滤器链匹配失败时,Warp会尝试匹配or后面的过滤器链。这允许你定义多个不同的路由处理逻辑,并根据请求的具体情况选择合适的逻辑来处理。

在你的代码中,通过连续使用.or运算,你定义了一个能够处理GET、POST、PUT和DELETE请求的路由逻辑。当Warp接收到一个请求时,它会首先尝试匹配GET请求和特定路径的过滤器链,如果匹配失败,则会尝试匹配POST请求的过滤器链,以此类推。

综上所述,routes变量通过组合多个过滤器来定义了一个复杂的路由处理逻辑,其中and运算用于串联过滤器并确保请求必须满足所有条件,而or运算则提供了备选的路由处理逻辑。

3. 关键代码分析

我们开发一下函数 and() 的实现:

    fn and<F>(self, other: F) -> And<Self, F>
    where
        Self: Sized,
        <Self::Extract as Tuple>::HList: Combine<<F::Extract as Tuple>::HList>,
        F: Filter + Clone,
        F::Error: CombineRejection<Self::Error>,
    {
        And {
            first: self,
            second: other,
        }
    }

继续看 And 这个数据类型的定义:

#[derive(Clone, Copy, Debug)]
pub struct And<T, U> {
    pub(super) first: T,
    pub(super) second: U,
}

impl<T, U> FilterBase for And<T, U>
where
    T: Filter,
    T::Extract: Send,
    U: Filter + Clone + Send,
    <T::Extract as Tuple>::HList: Combine<<U::Extract as Tuple>::HList> + Send,
    CombinedTuples<T::Extract, U::Extract>: Send,
    U::Error: CombineRejection<T::Error>,
{
    type Extract = CombinedTuples<T::Extract, U::Extract>;
    type Error = <U::Error as CombineRejection<T::Error>>::One;
    type Future = AndFuture<T, U>;

    fn filter(&self, _: Internal) -> Self::Future {
        AndFuture {
            state: State::First(self.first.filter(Internal), self.second.clone()),
        }
    }
}
...
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是一个使用 Rust 编写的简单的 RESTful API 示例,使用 Rocket 框架: 首先,确保您的 Rust 版本 >= 1.51,然后通过以下命令安装 Rocket: ``` $ cargo install rocket ``` 接下来,创建一个新的 Rust 项目: ``` $ cargo new my_rest_api --bin ``` 然后,在 `Cargo.toml` 文件中添加以下依赖项: ``` [dependencies] rocket = "0.5.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" ``` 现在,创建一个简单的 `main.rs` 文件: ```rust #![feature(proc_macro_hygiene, decl_macro)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; use rocket::State; use rocket_contrib::json::{Json, JsonValue}; #[derive(Serialize, Deserialize)] struct Message { id: u64, text: String, } #[get("/")] fn index() -> &'static str { "Hello, world!" } #[get("/message/<id>")] fn get_message(id: u64) -> Json<Message> { let message = Message { id, text: "Hello, world!".into(), }; Json(message) } #[post("/message", format = "json", data = "<message>")] fn create_message(message: Json<Message>, state: State<Vec<Message>>) -> JsonValue { let mut messages = state.inner().clone(); messages.push(message.into_inner()); state.replace(messages); json!({ "status": "ok" }) } fn main() { rocket::ignite() .manage(Vec::<Message>::new()) .mount("/", routes![index, get_message, create_message]) .launch(); } ``` 这个示例创建了一个简单的 RESTful API,支持 GET 和 POST 请求。它定义了一个 `Message` 结构体,用于序列化和反序列化 JSON 数据。`index` 函数返回一个简单的“Hello, world!”字符串。`get_message` 函数接受一个 `id` 参数,并返回一个 `Message` 对象。`create_message` 函数接受一个 `Message` 对象,并将其添加到一个状态管理的消息列表中。 最后,运行以下命令启动应用程序: ``` $ cargo run ``` 现在,您可以通过以下 URL 访问 API: - `GET /`:返回“Hello, world!”字符串。 - `GET /message/<id>`:返回具有指定 ID 的消息。 - `POST /message`:创建一个新的消息,例如:`{"id": 1, "text": "Hello, world!"}`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许野平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值