Rust:axum学习笔记(3) extract
接上一篇继续,今天学习如何从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 ¶ms {
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 ¶ms {
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 的更多相关文章
- Rust语言学习笔记(7)
模块 // 兄弟模块 mod network { fn connect() { } } mod client { fn connect() { } } // 父子模块 mod network { fn ...
- Rust语言学习笔记(6)
Traits(特质) // 特质 pub trait Summary { fn summarize(&self) -> String; } pub struct NewsArticle ...
- Rust语言学习笔记(5)
Structs(结构体) struct User { username: String, email: String, sign_in_count: u64, active: bool, } let ...
- Rust语言学习笔记(4)
Variables and Mutability(变量和可变性) 变量声明有三种:不变量(运行期的常量),变量以及(编译期的)常量. 变量可以重复绑定,后声明的变量覆盖前面声明的同名变量,重复绑定时可 ...
- 《Rust权威指南》学习笔记——4. 认识所有权
Rust权威指南学习笔记--认识所有权 什么是所有权 1. 所有权规则 Rust中的每一个值都有一个对应的变量作为它的所有者. 在同一时间内,值有且仅有一个所有者. 当所有者离开自己的作用域时,它持有 ...
- 两千行PHP学习笔记
亲们,如约而至的PHP笔记来啦~绝对干货! 以下为我以前学PHP时做的笔记,时不时的也会添加一些基础知识点进去,有时还翻出来查查. MySQL笔记:一千行MySQL学习笔记http://www.cnb ...
- 【工作笔记】BAT批处理学习笔记与示例
BAT批处理学习笔记 一.批注里定义:批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD,这些命令统称批处理命令. 二.常见的批处理指令: 命令清单: 1.RE ...
- MySQL存储过程学习笔记
MySQL在5.0以前并不支持存储过程,这使得MySQL在应用上大打折扣.MySQL 5.0终于开始支持存储过程了. MySQL的关键字大小写通用.该学习笔记对关键字使用大写:变量名,表名使用小写. ...
- 《SAS编程和数据挖掘商业案例》学习笔记# 19
继续<SAS编程与数据挖掘商业案例>学习笔记,本文側重数据处理实践.包含:HASH对象.自己定义format.以及功能强大的正則表達式 一:HASH对象 Hash对象又称散列表,是依据关键 ...
- Java多线程学习笔记--生产消费者模式
实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...
随机推荐
- linux系统权限管理
一.认识linux系统的文件权限 首先随便在一个目录下使用ls -l(可简写为ll)指令,就会把该目录下所有的文件和目录的权限显示出来,例如,在根目录下使用ls -l: (深蓝字:目录,白字:文件,浅 ...
- xamarin.forms+prism 实现DialogService(对话框服务)
Getting Started with IDialogAware Creating dialogs with the DialogService requires a ViewModel that ...
- 详解SLAM中的李群和李代数(上)
1 概述 最近阅读高翔大神的<视觉SLAM十四讲>这本书,感觉整本书写的非常的平实,用非常接地气的语言毫无保留的介绍了视觉SLAM的相关知识,非常值得一读.不过,在第4章出现的李群和李代数 ...
- 【HUST】网络攻防实践|5_二进制文件补丁技术|实验三 热补丁
文章目录 实验要求 实验过程 1. 64位Ubuntu下先安装32位库 2. 利用Preload Hook实现热补丁修补 3. 利用系统调用`ptrace`对运行状态的程序进行hook 3.1 编写补 ...
- Axure RP Element UI 2和 Element UI Plus元件库
基于ElementUI2.0及ElementUI Plus3.0二次创作的ElementUI 元件库.2个版本的原型图内容会有所不同,ElementUI Plus3.0的交互更加丰富和高级.你可以同时 ...
- Axis in DataFrame
Axis in DataFrame Optional parameter axis may appear in arithmetric between DataFrame and Series,the ...
- Java stream sorted自定义排序规则实现多字段排序
Stream 提供了丰富的操作(中间操作和终端操作)集合元素的轮子,但Stream流操作不影响原始集合数据,执行结果是一个新的集合对象.在<Java stream sorted使用 Comp ...
- IDEA 使用@Autowired提示Field injection is not recommended
摘要:IDEA 使用@Autowired提示Field injection is not recommended问题的解决办法. 在使用@Autowired注解进行bean注入,完成自动装配的工作 ...
- 来公司 3 年,被新来的技术大佬 PUA 了。。。
大家好,我是程序员鱼皮.最近收到一位鱼友的求助,感觉很有代表性,相信很多技术人都会遇到类似的情况.今天就和大家聊聊这个话题,看看遇到这种情况该怎么办. 鱼友提问 鱼皮哥,求解惑!最近我们公司新来了一个 ...
- AI智能文案助手ChatMoney:一键打造抖音爆款视频,助你轻松吸引千万级流量!
本文由 ChatMoney团队出品 引言 看着抖音上别人的视频轻松破百万点赞,是不是心里痒痒的?想知道他们是怎么做到的?其实,他们可能只是比您先一步掌握了这个秘密武器--ChatMoney.这不仅仅是 ...