Rust笔记:05-Rust使用hyper发送JSON HTTP请求

Rust笔记:05-Rust使用hyper发送JSON HTTP请求

1.背景

1.1 RUST hyper

hyper是一个偏底层的http库,支持HTTP/1和HTTP/2,支持异步Rust,并且同时提供了服务端和客户端的API支持. 很多同学可能觉得既然hyper是个偏底层的框架,那是不是就不需要去了解了呢?首先很多上层的框架, 比如rocket 和reqwest底层都是基于hyper的. 所以如果在使用这些框架的时候遇到了一些问题, 对hyper的了解肯定是有一定的帮助的.

在这篇文章中我们更加侧重于 http client 发送http 请求和 JSON 序列化

1.2 serde/serde_json

Serde是高效通用的对Rust数据结构进行序列化和反序列化的框架. Serde生态系统由知道如何序列化和反序列化自身的数据结构,以及知道如何序列化和反序列化其他事物的数据格式. Serde允许使用任何支持序列化和反序列化的基础数据类型组成的复杂数据结构.

1.3 rust json struct 转换工单

QuickType 是一款可以根据 json 文本生成指定语言(如 Type Script,C++,,Java,Rust,Go 等)类型声明代码的工具.

例如我们在写接口调用处理收到响应数据的逻辑时一般分为如下两步:

1.根据接口返回的 JSON 格式写一个对应的类型 2.写 JSON 格式验证与解析逻辑来根据收到的数据生成对应的类对象

使用 QuickType 工具就可以根据 JSON 文本帮助我们自动生成以上两部分的代码.

https://app.quicktype.io/

2.cargo.toml

[package]
name = "orderhunter"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = "0.5.10"
hyper = { version = "0.14.19", features = ["full"] }
tower = "0.4.13"
tokio = { version = "1.19", features = ["macros", "rt-multi-thread"] }
clap = { version = "3.2", features = ["derive","env"] }
anyhow = "1.0.58"
dotenv = "0.15.0"
env_logger = "0.9.0"
sqlx = { version = "0.6", features = ["mysql","runtime-tokio-native-tls",  "uuid", "time"] }
tower-http = { version = "0.2.0", features = ["trace"] }
serde = { version = "1.0.137", features = ["derive","std"] }
serde_json = "1.0.82"
multipart = { version = "0.18.0", features = ["hyper"] }

3. hyper http client 发送请求代码



use hyper::{body::Buf};
use serde::{Deserialize,Serialize};
use hyper::{Body, Method,Client,  Request};


#[derive(Debug, Serialize, Deserialize)]
pub struct GetResult {
    pub args: Option<Args>,`
    pub data: Option<String>,
    pub files: Option<Args>,
    pub form: Option<Args>,
    pub headers: Option<Headers>,
    pub json: Option<serde_json::Value>,
    pub origin: Option<String>,
    pub url: Option<String>,
}

// https://app.quicktype.io/

#[derive(Debug, Serialize, Deserialize)]
pub struct Args {
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Headers {
    #[serde(rename = "Accept")]
    pub accept: Option<String>,
    #[serde(rename = "Accept-Encoding")]
    pub accept_encoding: Option<String>,
    #[serde(rename = "Accept-Language")]
    pub accept_language: Option<String>,
    #[serde(rename = "Dnt")]
    pub dnt: Option<String>,
    #[serde(rename = "Host")]
    pub host: Option<String>,
    #[serde(rename = "Origin")]
    pub origin: Option<String>,
    #[serde(rename = "Referer")]
    pub referer: Option<String>,
    #[serde(rename = "Sec-Ch-Ua")]
    pub sec_ch_ua: Option<String>,
    #[serde(rename = "Sec-Ch-Ua-Mobile")]
    pub sec_ch_ua_mobile: Option<String>,
    #[serde(rename = "Sec-Ch-Ua-Platform")]
    pub sec_ch_ua_platform: Option<String>,
    #[serde(rename = "Sec-Fetch-Dest")]
    pub sec_fetch_dest: Option<String>,
    #[serde(rename = "Sec-Fetch-Mode")]
    pub sec_fetch_mode: Option<String>,
    #[serde(rename = "Sec-Fetch-Site")]
    pub sec_fetch_site: Option<String>,
    #[serde(rename = "User-Agent")]
    pub user_agent: Option<String>,
    #[serde(rename = "X-Amzn-Trace-Id")]
    pub x_amzn_trace_id: Option<String>,
}


// fn default_client()->hyper::Client{
//     Client::builder().pool_idle_timeout(Duration::from_sec(30)).build_http()
// }

#[derive(Serialize, Deserialize)]
struct Address {
    street: String,
    city: String,
}

async fn request_json_get(url: hyper::Uri) -> anyhow::Result<GetResult> {
    let client = Client::new();

    // Fetch the url...
    let res = client.get(url).await?;

    // asynchronously aggregate the chunks of the body
    let body = hyper::body::aggregate(res).await?;

    // try to parse as json with serde_json
    let get_result:GetResult = serde_json::from_reader(body.reader())?;

    Ok(get_result)
}

async fn request_json_post(uri:&str) -> anyhow::Result<GetResult> {
    let client = Client::new();

    let address = Address {
        street: "10 Downing Street".to_owned(),
        city: "London".to_owned(),
    };

    // Serialize it to a JSON string.
    let json_body = serde_json::to_vec(&address)?;

    let req = Request::builder().method(Method::POST).uri(uri)
    .header("X-Custom-Foo", "bar")
    .body(Body::from(json_body))?;



    // Fetch the url...
    let res = client.request(req).await?;

    // asynchronously aggregate the chunks of the body
    let body = hyper::body::aggregate(res).await?;

    // try to parse as json with serde_json
    let get_result:GetResult = serde_json::from_reader(body.reader())?;

    Ok(get_result)
}





#[cfg(test)]
mod tests {
    // 注意这个惯用法:在 tests 模块中,从外部作用域导入所有名字.
    use super::*;

    #[tokio::test]
    async fn test_json_get() {
        let url = "http://httpbin.org/get?a=1&b=true".parse::<hyper::Uri>().unwrap();
        println!("{:?}",url );
        let get_json = request_json_get(url).await.unwrap();
        // print users
        println!("users: {:#?}", get_json);
    }

    #[tokio::test]
    async fn test_json_post() {
        let url = "http://httpbin.org/post?a=1&b=true";
        let get_json = request_json_post(url).await.unwrap();
        // print users
        println!("users: {:#?}", get_json);
    }


}

The End

线上交流工具: 在你的terminal中输入 ssh $用户@mojotv.cn

在你的terminal中输入 ssh mojotv.cn hn 查看最新 hacknews

目录