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

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

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

[dependencies]
serde_json = "1"

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

#[derive(Debug, Serialize, Deserialize)]
struct Order {
//订单号
order_no: String,
//总金额
amount: f32,
//收货地址
address: String,
}

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

use serde::{Deserialize, Serialize};
use serde_json as sj;

接下来就可以使用了:

//序列化
let order = Order{
order_no:"1234567".to_string(),
amount:100.0,
address:"test".to_string()
};
let order_json =sj::to_string(&order).unwrap();
println!("{}",order_json); //反序列化
let order_json = r#"
{
"order_no": "1234567",
"amount": 100.0,
"address": "test"
}
"#;
let order:Order = sj::from_str(order_json).unwrap();
println!("{:?}",order); //下面少2个字段赋值,反序列化时,会报错
let order_json = r#"
{
"order_no": "1234567"
}
"#;
let order:Order = sj::from_str(order_json).unwrap();
println!("{:?}",order);

输出:

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

{"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

#[derive(Debug, Serialize, Deserialize)]
struct Order {
//订单号
order_no: String,
//总金额
amount: Option<f32>,
//收货地址
address: Option<String>,
}

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

    //下面少2个字段赋值,反序列化时,会报错
let order_json = r#"
{
"order_no": "1234567"
}
"#;
let order: Order = sj::from_str(order_json).unwrap();
println!("{:?}", order);

输出:

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

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

路由:

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

处理函数:

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

也可以这样:

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

1.2 多参数提取

路由:

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

处理函数:

// 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提取

路由:

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

 处理函数:

#[derive(Deserialize)]
struct SomeRequest {
a: String,
b: i32,
c: String,
d: u32,
} // eg: path_req/a1/b1/c1/d1
async fn path_req(Path(req): Path<SomeRequest>) -> String {
format!("a:{},b:{},c:{},d:{}", req.a, req.b, req.c, req.d)
}

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

 

二、从queryString里提取内容

路由:

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

处理函数:

//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

#[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,参考下面的代码:

路由:

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

处理函数:

//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表单提交提取内容

路由:

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

处理函数:

// 表单提交
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提取内容

路由:

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

处理函数:

// 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头

路由:

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

处理函数:

/**
* 获取所有请求头
*/
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

路由:

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

处理函数 :

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

 

五、cookie读写

路由:

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

处理函数:

/**
* 设置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依赖项:

[dependencies]
axum = { version="0.4.3", features = ["headers"] }
tokio = { version="1", features = ["full"] }
serde = { version="1", features = ["derive"] }
serde_json = "1"
http = "0.2.1"
headers = "0.3"

main.rs

use std::collections::HashMap;

use axum::{
extract::{Form, Path, Query, TypedHeader},
http::header::{HeaderMap, HeaderValue},
response::Json,
routing::{get, post},
Router,
};
use http::StatusCode;
use serde::Deserialize; // eg: /user/30,将解析出id=30
async fn user_info(Path(id): Path<i32>) -> String {
format!("user id:{}", id)
} // eg: /user2/30,将解析出id=30
async fn user_info_2(id: Path<i32>) -> String {
format!("user id:{}", id.0)
} // eg: /person/123/30,将解析出id=123, age=30
async fn person(Path((id, age)): Path<(i32, i32)>) -> String {
format!("id:{},age:{}", id, age)
} #[derive(Deserialize)]
struct SomeRequest2 {
a: Option<String>,
b: Option<i32>,
c: Option<String>,
d: Option<u32>,
} #[derive(Deserialize)]
struct SomeRequest {
a: String,
b: i32,
c: String,
d: u32,
} // eg: path_req/a1/b1/c1/d1
async fn path_req(Path(req): Path<SomeRequest>) -> String {
format!("a:{},b:{},c:{},d:{}", req.a, req.b, req.c, req.d)
} //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)
} //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()
)
} //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)
} // 表单提交
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()
)
} // json提交
async fn json_request(Json(model): Json<SomeRequest>) -> String {
format!("a:{},b:{},c:{},d:{}", model.a, model.b, model.c, model.d)
} /**
* 获取所有请求头
*/
async fn get_all_header(headers: HeaderMap) -> String {
for (key, value) in &headers {
println!("key:{:?} , value:{:?}", key, value);
}
format!("{:?}", headers)
} /**
* 获取http headers中的user_agent头
*/
async fn get_user_agent_header(TypedHeader(user_agent): TypedHeader<headers::UserAgent>) -> String {
user_agent.to_string()
} /**
* 设置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());
} #[tokio::main]
async fn main() {
// our router
let app = Router::new()
.route("/user/:id", get(user_info))
.route("/user2/:id", get(user_info_2))
.route("/person/:id/:age", get(person))
.route("/path_req/:a/:b/:c/:d", get(path_req))
.route("/query_req", get(query_req))
.route("/query_req2", get(query_req2))
.route("/query", get(query))
.route("/form", post(form_request))
.route("/json", post(json_request))
.route("/header", get(get_all_header))
.route("/user_agent", get(get_user_agent_header))
.route("/set_cookie", get(set_cookie_and_redirect))
.route("/get_cookie", get(get_cookie)); // run it with hyper on localhost:3000
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}

  

参考文档:

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

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

Rust:axum学习笔记(3) extract 的更多相关文章

  1. Rust语言学习笔记(7)

    模块 // 兄弟模块 mod network { fn connect() { } } mod client { fn connect() { } } // 父子模块 mod network { fn ...

  2. Rust语言学习笔记(6)

    Traits(特质) // 特质 pub trait Summary { fn summarize(&self) -> String; } pub struct NewsArticle ...

  3. Rust语言学习笔记(5)

    Structs(结构体) struct User { username: String, email: String, sign_in_count: u64, active: bool, } let ...

  4. Rust语言学习笔记(4)

    Variables and Mutability(变量和可变性) 变量声明有三种:不变量(运行期的常量),变量以及(编译期的)常量. 变量可以重复绑定,后声明的变量覆盖前面声明的同名变量,重复绑定时可 ...

  5. 《Rust权威指南》学习笔记——4. 认识所有权

    Rust权威指南学习笔记--认识所有权 什么是所有权 1. 所有权规则 Rust中的每一个值都有一个对应的变量作为它的所有者. 在同一时间内,值有且仅有一个所有者. 当所有者离开自己的作用域时,它持有 ...

  6. 两千行PHP学习笔记

    亲们,如约而至的PHP笔记来啦~绝对干货! 以下为我以前学PHP时做的笔记,时不时的也会添加一些基础知识点进去,有时还翻出来查查. MySQL笔记:一千行MySQL学习笔记http://www.cnb ...

  7. 【工作笔记】BAT批处理学习笔记与示例

    BAT批处理学习笔记 一.批注里定义:批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD,这些命令统称批处理命令. 二.常见的批处理指令: 命令清单: 1.RE ...

  8. MySQL存储过程学习笔记

    MySQL在5.0以前并不支持存储过程,这使得MySQL在应用上大打折扣.MySQL 5.0终于开始支持存储过程了. MySQL的关键字大小写通用.该学习笔记对关键字使用大写:变量名,表名使用小写. ...

  9. 《SAS编程和数据挖掘商业案例》学习笔记# 19

    继续<SAS编程与数据挖掘商业案例>学习笔记,本文側重数据处理实践.包含:HASH对象.自己定义format.以及功能强大的正則表達式 一:HASH对象 Hash对象又称散列表,是依据关键 ...

  10. Java多线程学习笔记--生产消费者模式

    实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...

随机推荐

  1. 【HUST】网安|软件安全课设|记录

    仓库链接 clone之后点开html文件即可使用. 效果如下图: 文章目录 进程通信设计 共享内存(Windows) 初始化共享内存 修改和读取共享内存的内容 共享内存(linux) (尝试使用,但使 ...

  2. ufw配置自动管理端口转发和DNAT+MASQUERADE

    端口A转发到本地的端口B 端口A转发到另一台机器的端口B(需借助DNAT) 一般情况下, 我们配置ufw来实现端口转发时会在修改 /etc/ufw/before.rules 文件, 增加*nat部分. ...

  3. 正点原子ALPHA开发板使用4.3寸触摸屏LCD驱动实验显示不正常

    显示问题 裸机开发时,驱动教程的PDF里给了4.3寸LCD屏幕的设置参数.如下图所示: 但是按照这个设置,编写设备树dts文件,下载到开发板里,却出现了显示异常,具体来说就是帧率不对,图和字都是歪斜的 ...

  4. 保姆教程系列:生成 SSH Key 并配置连接远程仓库

    @ 目录 前言 第 1 步:检查是否已有 SSH Key 第 2 步:生成新的 SSH Key 第 3 步:启动 SSH Agent 并添加密钥 第 4 步:复制 SSH 公钥 第 5 步:添加 SS ...

  5. 浅析Java8中default关键字

    摘要:介绍Java8新增关键字default,它用于在接口中标记方法为默认方法和编写实现逻辑,方便通过新增方法重构接口,而无需修改所有实现类,目的在于兼容接口已有实现类. 综述   default关键 ...

  6. protobuf 'NoneType' object has no attribute 'message_types_by_name'

    最近爬一个网站,用的protobuf协议,报错查了半天.报错'NoneType' object has no attribute 'message_types_by_name',最后是因为protob ...

  7. java XML字符串和bean实体类互转

    pom引入依赖 <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artif ...

  8. Scrum Master,这九个问题你问了吗?

    从团队技术负责人到Scrum Master或PO,我们需要从做决策转为提问题. 一.2个关于估算的问题 团队在进行项目前需要进行粗略估算,但这并不是要求团队成员一定按照估算出的结果进行. 问题一:估算 ...

  9. C++项目属性配置Tips

    1.项目属性->VC++目录->包含目录 & 库目录 这里的"包含目录"."库目录"编辑之后是全局的: 2.项目属性->C/C++-& ...

  10. 菜鸟入门bootstrap

    1.入门 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&q ...