warp框架教程3-path, method和自定义请求方法

本文介绍了如何使用Warp框架中的path和method模块来构建RESTful风格的API,包括匹配路由、处理HTTP方法以及自定义请求方法。通过示例展示了创建、登录、退出、编辑和删除用户等操作的实现,强调了and_then和map方法的使用,以及Filter的组合和自定义错误处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

path, method和自定义请求方法

path 是 warp 中的路由系统, 一个 web 框架的灵魂所在, 一个优美的路由系统可以给我们带来非常良好的使用体验, 而 warp 的路由体验本身就是非常 nice 的。在本文中将展示一个 RESTful 风格的 API 设计。下面先来学习一下 path 模块。

path 模块

path 文档如下所示:
在这里插入图片描述

  • path 是匹配路由的方法,path! 是一个宏,它能更简单的来匹配路由,但是它的限制比较少,在实际使用中,我们更偏向于使用 path 方法。
  • param 是提取路径参数的方法,例如 user/12345, 使用 param 方法可以获取到路径参数 12345;
  • end 用来指定路径匹配结束,例如 user/12345/321 将是一个无效的路径。

这个模块中还有其他的一些结构体和方法,具体可以参考文档。
在这里插入图片描述

method 模块

method 模块处理请求的 HTTP 方法部分,如果请求方法不匹配,将拒绝请求 并带有返回 405 Method Not Allowed. 这个模块提供了常见的 HTTP 请求方法,如下图所示:
在这里插入图片描述
我们在使用 filter 的时候,通常指定某个路由上的 method 来进行 RESTFul API设计。

自定义请求方法

在 warp 中自定义请求方法也非常简单,使用如下的代码段即可实现。

use warp::{hyper::Method, reject, Filter, Rejection, Reply};

// 定义CREATE 和 LOGOUT 方法
const CREATE_METHOD: &'static str = "CREATE";
const LOGOUT_METHOD: &'static str = "LOGOUT";

// 定义Method ERROR,为其实现 Reject 即可自动实现 405 Method Not Allowed.
#[derive(Debug)]
struct MethodError;
impl warp::reject::Reject for MethodError {}

// 实现自定义方法函数
fn method(name: &'static str) -> impl Filter<Extract = (), Error = warp::Rejection> + Clone {
    warp::method()
        .and_then(move |m: Method| async move {
            if m == name {
                Ok(())
            } else {
                Err(warp::reject::custom(MethodError))
            }
        })
        .untuple_one()
}

实现自定义 HTTP Method 是非常简单的,需要的注意的有两点

  1. 为自定义 Method 实现 Method Error,这要求为其实现 warp::reject::Reject 特征。
  2. 自定义请求方法函数的返回值必须是 impl Filter<Extract = (), Error = warp::Rejection> + Clone,这样表明返回的是一个 Filter,这也是我们在warp中定义中间件是返回的类型。在下一篇中,我们来介绍一下Filter中的方法。
  3. 如果需要其他的逻辑,可以在 m == name 的块内实现。
  4. 自定义method存在bug,见https://github.com/seanmonstar/warp/issues/1051

构建RESTful API

有了上面的知识,我们现在来构建一个RESTful 风格的 API。

    let user_router = warp::path("user");    // user 模块的根路径

    let create_user = user_router
        .and(warp::path::end())
        .and(method(CREATE_METHOD))
        .and_then(create_user);

    let login_user = user_router
        .and(warp::path::end())
        .and(warp::post())
        .and_then(login_user);

    let logout_user = user_router
        .and(warp::path::end())
        .and(method(LOGOUT_METHOD))
        .and_then(logout_user);    
    
    let edit_user = user_router
        .and(warp::path::param())
        .and(warp::path::end())
        .and(warp::put())
        .and_then(edit_user);        

    let delete_user = user_router
        .and(warp::path::param())
        .and(warp::path::end())
        .and(warp::delete())
        .and_then(delete_user);     
        
    let apis = hello
        .or(create_user)
        .or(login_user)
        .or(logout_user)
        .or(edit_user)
        .or(delete_user);

上面这段代码我们定义了RESTFul 风格的 API,我们使用了前文提到的 path, method,自定义 method。我们的五个处理函数分别对应5个HTTP方法。

  • and_then 方法是用来添加异步函数的,和它对应的是 map 方法,我们在第一篇文章中使用过,map方法是用来添加同步函数的。
  • and 方法添加的 Filter 之间的关系是 and 关系(看起来像是废话)
  • or 方法添加的 FIlter 之间的关系是 or 关系(看起来像是废话)
函数HTTP 方法
create_userCREATE
login_userPOST
logout_userLOGOUT
edit_userPUT
delete_userDELETE

对应的五个函数实现,如下所示:

async fn create_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("创建用户".to_string())
}

async fn login_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("用户登录".to_string())
}

async fn logout_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("用户退出".to_string())
}

async fn edit_user(id: u32) -> Result<impl warp::Reply, warp::Rejection> {
    Ok(format!("修改用户{}信息", id))
}

async fn delete_user(id: u32) -> Result<impl warp::Reply, warp::Rejection> {
    Ok(format!("注销用户{}信息", id))
}
  • 再次强调,and_then 方法需要一个 Future 对象,而 map 方法需要一个 called Func 对象。它们需要的返回值是 Result<impl warp::Reply, warp::Rejection> 类型。返回值必须是warp 可以 Reply 的。

完整的代码如下所示:

use std::env;
use warp::{Filter, hyper::Method};

#[tokio::main]
async fn main() {
    env::set_var("MYAPP_LOG", "INFO");
    pretty_env_logger::try_init_timed_custom_env("MYAPP_LOG").expect("logger init failed!");
    let log = warp::log("MYAPP_LOG");

    // GET /hello/warp => 200 OK with body "Hello, warp!"
    let hello = warp::path!("hello" / String)
        .and(warp::addr::remote())
        .and(warp::header("x-forwarded-for"))
        .and(warp::header("x-real-ip"))
        .map(|name: String, addr: Option<std::net::SocketAddr>, x_forward_for: String, x_real_ip: String|
            {
                format!("Hello, {}!\nClient IP: {}\nX-Forwarded-For: {}\nX-Real-IP: {}\n", 
                    name, addr.unwrap().to_string(), x_forward_for, x_real_ip)
            })
        .with(log);


    let user_router = warp::path("user");    // user 模块的根路径

    let create_user = user_router
        .and(warp::path::end())
        .and(method(CREATE_METHOD))
        .and_then(create_user);

    let login_user = user_router
        .and(warp::path::end())
        .and(warp::post())
        .and_then(login_user);

    let logout_user = user_router
        .and(warp::path::end())
        .and(method(LOGOUT_METHOD))
        .and_then(logout_user);    
    
    let edit_user = user_router
        .and(warp::path::param())
        .and(warp::path::end())
        .and(warp::put())
        .and_then(edit_user);        

    let delete_user = user_router
        .and(warp::path::param())
        .and(warp::path::end())
        .and(warp::delete())
        .and_then(delete_user);        

    let apis = hello
        .or(create_user)
        .or(login_user)
        .or(logout_user)
        .or(edit_user)
        .or(delete_user);



    warp::serve(apis)
        .run(([127, 0, 0, 1], 3030))       // 监听 127.0.0.1
        .await;
}

const CREATE_METHOD: &'static str = "CREATE";
const LOGOUT_METHOD: &'static str = "LOGOUT";

#[derive(Debug)]
struct MethodError;
impl warp::reject::Reject for MethodError {}

fn method(name: &'static str) -> impl Filter<Extract = (), Error = warp::Rejection> + Clone {
    warp::method()
        .and_then(move |m: Method| async move {
            if m == name {
                Ok(())
            } else {
                Err(warp::reject::custom(MethodError))
            }
        })
        .untuple_one()
}

async fn create_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("创建用户".to_string())
}

async fn login_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("用户登录".to_string())
}

async fn logout_user() -> Result<impl warp::Reply, warp::Rejection> {
    Ok("用户退出".to_string())
}

async fn edit_user(id: u32) -> Result<impl warp::Reply, warp::Rejection> {
    Ok(format!("修改用户{}信息", id))
}

async fn delete_user(id: u32) -> Result<impl warp::Reply, warp::Rejection> {
    Ok(format!("注销用户{}信息", id))
}

使用postman进行测试的结果如下所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
OK,到这里,我们非常简单的 RESTFul API已经设计完毕了。使用 warp 来办到这些是非常简单的,并且我们很容易就支持了自定义的 HTTP Method。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值