Rust:axum学习笔记(3) extract 

上一篇继续,今天学习如何从Request请求中提取想要的内容,用axum里的概念叫Extract。

预备知识:json序列化/反序列化

鉴于现在web开发中,json格式被广泛使用,先熟悉下rust中如何进行json序列化/反序列化。

1
2
[dependencies]
serde_json = "1"

先加入serde_json依赖项,然后就可以使用了,先定义1个struct:

注意:别忘了加#[derive(Debug, Serialize, Deserialize)],这个表示被修饰的struct,实现了序列化/反序列化,以及"{:?}"调试输出的能力,当然最开头要use一下:

接下来就可以使用了:

输出:

****************************

{"order_no":"1234567","amount":100.0,"address":"test"}
Order { order_no: "1234567", amount: 100.0, address: "test" }
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("missing field `amount`", line: 4, column: 9)', request/src/main.rs:198:48

****************************

可以看到,相比于java等其它语言的jackson, gson之类的json类库,rust中的serde非常严格,少1个字段反序列化时都会报错,因此建议定义struct时,对于可能为空的字段,最好加Option

这回再反序列化时,就不会报错了:

输出:

Order { order_no: "1234567", amount: None, address: None }

 

一、从path中提取内容
1.1 单一参数提取

路由:

1
.route("/user/:id", get(user_info))

处理函数:

1
2
3
4
// eg: /user/30,将解析出id=30
async fn user_info(Path(id): Path<i32>) -> String {
    format!("user id:{}", id)
}

也可以这样:

1
2
3
4
// eg: /user2/30,将解析出id=30
async fn user_info_2(id: Path<i32>) -> String {
    format!("user id:{}", id.0)
}

1.2 多参数提取

路由:

1
.route("/person/:id/:age", get(person))

处理函数:

1
2
3
4
// eg: /person/123/30,将解析出id=123, age=30
async fn person(Path((id, age)): Path<(i32, i32)>) -> String {
    format!("id:{},age:{}", id, age)
}

用(X,Y)之类的tuple来提取参数,但是如果参数很多,通常会将参数对象化,封装成一个struct

1.3 struct提取

路由:

1
.route("/path_req/:a/:b/:c/:d", get(path_req))

 处理函数:

不过这种方法,必须要求所有参数都有,比如:http://localhost:3000/path_req/abc/2/yjmyzz/4,如果少1个参数,比如:http://localhost:3000/path_req/abc/2/yjmyzz 则会路由匹配失败

 

二、从queryString里提取内容

路由:

1
.route("/query_req", get(query_req))

处理函数:

1
2
3
4
//eg: query_req/?a=test&b=2&c=abc&d=80
async fn query_req(Query(args): Query<SomeRequest>) -> String {
    format!("a:{},b:{},c:{},d:{}", args.a, args.b, args.c, args.d)
}

注意:按上面的处理方式,QueryString里必须同时有a, b, c, d这几个参数,否则会报错。如果希望有些参数可为空,则需要把SomeRequest按前面提到的,相应的字段改成Option

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#[derive(Deserialize)]
struct SomeRequest2 {
    a: Option<String>,
    b: Option<i32>,
    c: Option<String>,
    d: Option<u32>,
}
 
//eg: query_req2?a=abc&c=中华人民共和国&d=123
async fn query_req2(Query(args): Query<SomeRequest2>) -> String {
    format!(
        "a:{},b:{},c:{},d:{}",
        args.a.unwrap_or_default(),
        args.b.unwrap_or(-1), //b缺省值指定为-1
        args.c.unwrap_or_default(),
        args.d.unwrap_or_default()
    )
}

有时候,可能想获取所有的QueryString参数,可以用HashMap,参考下面的代码:

路由:

1
.route("/query", get(query))

处理函数:

1
2
3
4
5
6
7
//eg: query?a=1&b=1.0&c=xxx
async fn query(Query(params): Query<HashMap<String, String>>) -> String {
    for (key, value) in &params {
        println!("key:{},value:{}", key, value);
    }
    format!("{:?}", params)
}

  

三、从Form表单提交提取内容

路由:

1
.route("/form", post(form_request))

处理函数:

1
2
3
4
5
6
7
8
9
10
// 表单提交
async fn form_request(Form(model): Form<SomeRequest2>) -> String {
    format!(
        "a:{},b:{},c:{},d:{}",
        model.a.unwrap_or_default(),
        model.b.unwrap_or(-1), //b缺省值指定为-1
        model.c.unwrap_or_default(),
        model.d.unwrap_or_default()
    )
}

  

四、从applicataion/json提取内容

路由:

1
.route("/json", post(json_request))

处理函数:

1
2
3
4
// json提交
async fn json_request(Json(model): Json<SomeRequest>) -> String {
    format!("a:{},b:{},c:{},d:{}", model.a, model.b, model.c, model.d)
}

  

五、提取HttpHeader

5.1 提取所有header头

路由:

1
.route("/header", get(get_all_header))

处理函数:

1
2
3
4
5
6
7
8
9
/**
 * 获取所有请求头
 */
async fn get_all_header(headers: HeaderMap) -> String {
    for (key, value) in &headers {
        println!("key:{:?} , value:{:?}", key, value);
    }
    format!("{:?}", headers)
}

5.2 提取指定header头,比如user-agent

路由:

1
.route("/user_agent", get(get_user_agent_header))

处理函数 :

1
2
3
4
5
6
/**
 * 获取http headers中的user_agent头
 */
async fn get_user_agent_header(TypedHeader(user_agent): TypedHeader<headers::UserAgent>) -> String {
    user_agent.to_string()
}

 

五、cookie读写

路由:

1
2
.route("/set_cookie", get(set_cookie_and_redirect))
.route("/get_cookie", get(get_cookie));

处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
 * 设置cookie并跳转到新页面
 */
async fn set_cookie_and_redirect(mut headers: HeaderMap) -> (StatusCode, HeaderMap, ()) {
    //设置cookie,blog_url为cookie的key
    headers.insert(
        axum::http::header::SET_COOKIE,
        HeaderValue::from_str("blog_url=http://yjmyzz.cnblogs.com/").unwrap(),
    );
 
    //重设LOCATION,跳到新页面
    headers.insert(
        axum::http::header::LOCATION,
        HeaderValue::from_str("/get_cookie").unwrap(),
    );
    //302重定向
    (StatusCode::FOUND, headers, ())
}
 
/**
 * 读取cookie
 */
async fn get_cookie(headers: HeaderMap) -> (StatusCode, String) {
    //读取cookie,并转成字符串
    let cookies = headers
        .get(axum::http::header::COOKIE)
        .and_then(|v| v.to_str().ok())
        .map(|v| v.to_string())
        .unwrap_or("".to_string());
 
    //cookie空判断
    if cookies.is_empty() {
        println!("cookie is empty!");
        return (StatusCode::OK, "cookie is empty".to_string());
    }
 
    //将cookie拆成列表
    let cookies: Vec<&str> = cookies.split(';').collect();
    println!("{:?}", cookies);
    for cookie in &cookies {
        //将内容拆分成k=v的格式
        let cookie_pair: Vec<&str> = cookie.split('=').collect();
        if cookie_pair.len() == 2 {
            let cookie_name = cookie_pair[0].trim();
            let cookie_value = cookie_pair[1].trim();
            println!("{:?}", cookie_pair);
            //判断其中是否有刚才设置的blog_url
            if cookie_name == "blog_url" && !cookie_value.is_empty() {
                println!("found:{}", cookie_value);
                return (StatusCode::OK, cookie_value.to_string());
            }
        }
    }
    return (StatusCode::OK, "empty".to_string());
}

 

最后,附上述示例完整代码:

cargo.toml依赖项:

main.rs

  

参考文档:

https://docs.rs/axum/latest/axum/#extractors

https://github.com/tokio-rs/axum/tree/main/examples

posted @   菩提树下的杨过  阅读(3110)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
历史上的今天:
2014-01-01 maven学习(上)- 基本入门用法
2008-01-01 [转贴]C#中实现窗体间传值
点击右上角即可分享
微信分享提示