前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >warp框架教程3-path, method和自定义请求方法

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

作者头像
zy010101
发布2023-07-11 13:52:46
5120
发布2023-07-11 13:52:46
举报
文章被收录于专栏:程序员

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 中自定义请求方法也非常简单,使用如下的代码段即可实现。

代码语言:javascript
复制
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 的块内实现。

构建RESTful API

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

代码语言:javascript
复制
    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_user

CREATE

login_user

POST

logout_user

LOGOUT

edit_user

PUT

delete_user

DELETE

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

代码语言:javascript
复制
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 的。

完整的代码如下所示:

代码语言:javascript
复制
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。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • path, method和自定义请求方法
    • path 模块
      • method 模块
        • 自定义请求方法
          • 构建RESTful API
          相关产品与服务
          消息队列 TDMQ
          消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档