首页 > 其他分享 >rust HTTP客户端reqwest快速入门

rust HTTP客户端reqwest快速入门

时间:2023-12-02 15:32:27浏览次数:27  
标签:HTTP url resp await reqwest let main rust

这篇文章主要是收集了reqwest一些常用的代码片段,便于以后直接复制使用,对标之前go语言的net/http客户端的快速入门教程。

reqwest版本: 0.11.22

参考依赖配置:

[dependencies]
reqwest = { version="0.11.22", features=["json", "multipart"]}
tokio = { version = "1", features = ["full"] }
serde_json = "1.0.107"

下面代码使用的请求地址https://youerning.top对于一部分代码可能不起作用, 比如上传表单之类的请求,大家测试的时候需要更换请求的地址。

快速入门

// https://youerning.top/post/reqwest-tutorial/
use reqwest::Result;

#[tokio::main]
async fn main() -> Result<()>{
    let body = reqwest::get("https://youerning.top")
    .await?
    .text()
    .await?;

    println!("body: {}", body);
    Ok(())
}

如果不自己构建client对象, reqwest默认只提供get方法, 在库层面,reqwest只暴露了get方法, 如果需要使用其他的方法可以自己构造客户端。

请求前

查询参数

查询参数值一般指url中问号后的部分, 比如https://youerning.top/?ie=UTF-8&wd=test中的ie=UTF-8&wd=test

use std::collections::HashMap;
use reqwest::Result;

#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let client = reqwest::Client::new();
    let mut params = HashMap::new();
    params.insert("key", "value");
    let resp = client
        .get(url)
        .query(&params)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

http请求头参数

常见的http请求头有User-Agent, 用于简单的反爬以及反反爬。

reqwest同时支持两种设定请求头的方式,方法如下:

use std::collections::HashMap;
use reqwest::Result;
use reqwest::header;


#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let client = reqwest::Client::new();
    let mut headers = header::HeaderMap::new();
    headers.insert("custom", header::HeaderValue::from_static("youerning.top"));
    let resp = client
        .get(url)
        .header("User-Agent", "youerning.top")
        .headers(headers)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

请求体参数

既然有了查询参数为啥还需要请求体参数? 因为查询参数在url中,总不可能上传个文件也把文件的编码到url中,那么这个url太长了,并且url的长度有有限制的。

一般来说,常用的请求体参数有以下三种。

  1. 表单 对应的Content-Type是application/x-www-form-urlencoded
  2. json 对应的Content-Type是application/json
  3. 包含文件的表单 对应的Content-Type是multipart/form-data

上传表单

use reqwest::Result;

#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let client = reqwest::Client::new();
    let mut params = HashMap::new();
    params.insert("key2", "value2");
    let resp = client
        .get(url)
        .form(&[("key1", "value1")])
        .form(&params)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

reqwest支持两种方式设置form, 但是不能像headers那样反复追加,这里因为.form(&params)最后调用,所以上传的表单只有key2=value2

JSON请求

rust不像golang那样内置了很多实用的标准库,rust很多功能需要依赖外部库,比如这里的json, 在reqwest中我们需要引入serde_json, 当然也可以不使用serde_json而是使用hashmap, json和表单一样后面的会覆盖前面的调用。

如果需要使用json方法,reqwest的依赖需要启用json特性!

use serde_json;
use reqwest::Result;
use std::collections::HashMap;


#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let client = reqwest::Client::new();
    let mut payload = HashMap::new();
    payload.insert("key2", "value2");
    let resp = client
        .post(url)
        .json(&serde_json::json!({
            "key1": "value1"
        }))
        .json(&payload)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

上传文件的表单

use reqwest::Result;
use reqwest::multipart::{Form, Part};

#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let client = reqwest::Client::new();
    let form: Form = Form::new();
    // 也可以不指定mime
    let file = Part::text("file").file_name("test.txt").mime_str("text/plain").unwrap();
    let form = form.part("uploadfile", file);
    let resp = client
        .post(url)
        .multipart(form)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

编码后的请求体内容如下:

--e0f1b497af95f167-38dc8fccee705209-d805583dc901b5f7-4bb5deae2f0db3bb
Content-Disposition: form-data; name="uploadfile"; filename="test.txt"
Content-Type: text/plain

file
--e0f1b497af95f167-38dc8fccee705209-d805583dc901b5f7-4bb5deae2f0db3bb--

上传表单带有文件的表单还是比较复杂的。

Cookie

默认情况下reqwest也是不会启用cookie这个特性的, 所以需要使用cookie的话,要在reqwest的依赖设置中设置cookie这个依赖, 比如reqwest = { version="0.11.22", features=["json", "multipart", "cookies"]}, 除此之前还需要一个额外的库来创建cookie, 也就是reqwest_cookie_store

这个cookie我没使用明白,这里只是简单的copy一下reqwest_cookie_store的示例代码

更详细的例子可以查看: https://docs.rs/reqwest_cookie_store/latest/reqwest_cookie_store/

// Load an existing set of cookies, serialized as json
let cookie_store = {
  if let Ok(file) = std::fs::File::open("cookies.json")
    .map(std::io::BufReader::new)
    {
      // use re-exported version of `CookieStore` for crate compatibility
      reqwest_cookie_store::CookieStore::load_json(file).unwrap()
    }
    else
    {
      reqwest_cookie_store::CookieStore::new(None)
    }
};
let cookie_store = reqwest_cookie_store::CookieStoreMutex::new(cookie_store);
let cookie_store = std::sync::Arc::new(cookie_store);
{
  // Examine initial contents
  println!("initial load");
  let store = cookie_store.lock().unwrap();
  for c in store.iter_any() {
    println!("{:?}", c);
  }
}

// Build a `reqwest` Client, providing the deserialized store
let client = reqwest::Client::builder()
    .cookie_provider(std::sync::Arc::clone(&cookie_store))
    .build()
    .unwrap();

// Make a sample request
client.get("https://google.com").send().await.unwrap();
{
  // Examine the contents of the store.
  println!("after google.com GET");
  let store = cookie_store.lock().unwrap();
  for c in store.iter_any() {
    println!("{:?}", c);
  }
}

超时

超时可以很简单的设置,比如

use std::time;
use reqwest::Result;

#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let client = reqwest::Client::new();
    let resp = client
        .post(url)
        .timeout(time::Duration::from_secs(1))
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

这里设置了一个总的超时时间。

SSL证书

对于自签名证书最常见的就是不验证证书,代码如下

use reqwest::Result;

#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    
    let client = reqwest::Client::builder().danger_accept_invalid_certs(true).build().unwrap();
    let resp = client
        .get(url)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

不验证证书肯定是不安全的,所以可以加载自签名证书, 代码如下

use std::fs::File;
use std::io::Read;
use reqwest::Result;

#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";

     let mut buf = Vec::new();
     File::open("my_cert.pem").unwrap()
         .read_to_end(&mut buf).unwrap();
     let cert = reqwest::Certificate::from_pem(&buf)?;
    let client = reqwest::Client::builder().add_root_certificate(cert).build().unwrap();
    
    let resp = client
        .get(url)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

代理

直接抄的官方的example的代码,不想写了。。。。。

#![deny(warnings)]


#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    // Make sure you are running tor and this is your socks port
    let proxy = reqwest::Proxy::all("socks5h://127.0.0.1:9050").expect("tor proxy should be there");
    let client = reqwest::Client::builder()
        .proxy(proxy)
        .build()
        .expect("should be able to build reqwest client");

    let res = client.get("https://check.torproject.org").send().await?;
    println!("Status: {}", res.status());

    let text = res.text().await?;
    let is_tor = text.contains("Congratulations. This browser is configured to use Tor.");
    println!("Is Tor: {}", is_tor);
    assert!(is_tor);

    Ok(())
}

重定向

有时候可以限制重定向的测试来避免重定向次数过多。

use reqwest::Result;
use reqwest::redirect::Policy;



#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    let policy = Policy::custom(|attempt| {
        if attempt.previous().len() > 5 {
            attempt.error("too many redirects")
        } else if attempt.url().host_str() == Some("example.domain") {
            // prevent redirects to 'example.domain'
            attempt.stop()
        } else {
            attempt.follow()
        }
    });
    
    let client = reqwest::Client::builder().redirect(policy).build().expect("build client failed");
    
    let resp = client
        .get(url)
        .send()
        .await?
        .text()
        .await?;

    println!("resp: {}", resp);
    Ok(())
}

reqwest 默认的重定向检查是10次。

请求后

请求后会获得一个Response对象, 这个结构体有许多比较有用的字段。

响应头信息/状态码

响应头信息可以用于一些特殊字段的判断,比如字符集,而状态码可以简单的判断请求是否成功,

use reqwest::Result;


#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    
    let resp = reqwest::Client::new()
        .get(url)
        .send()
        .await?;

    println!("headers: {:?}", resp.headers());
    println!("status: {}", resp.status());
    Ok(())
}

而作者判断响应码给出了下面这个例子

use reqwest::Error;

async fn handle_error() -> Result<(), Error> {
    let response = reqwest::get("https://www.example.com").await?;

    match response.status().as_u16() {
        200..=299 => {
            let body = response.text().await?;
            println!("Success! Body:\n{}", body);
        }
        400..=599 => {
            let status = response.status();
            let error_message = response.text().await?;
            println!("Error {}: {}", status, error_message);
        }
        _ => {
            println!("Unexpected status code: {}", response.status());
        }
    }

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    handle_error().await?;
    Ok(())
}

响应体

如果默认响应体的编码是utf8,就可以直接处理响应体, 可以通过text方法直接获得解码后的String对象

use reqwest::Result;


#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    
    let resp = reqwest::Client::new()
        .get(url)
        .send()
        .await?;

    println!("resp content: {}", resp.text().await?);
    Ok(())
}

reqwest的text方法默认使用utf8解码

编码

一般来说大家都是用utf-8了,但是总有一些例外情况,所以为了安全的解析响应体内容,需要检测响应体的编码格式 如果我们需要自己解析字节流,我们可以通过bytes获得字节流,但是,如果我们知道对应的编码,则可以通过text_with_charset方法来解码。

use reqwest::Result;


#[tokio::main]
async fn main() -> Result<()>{
    let url = "https://youerning.top";
    
    let resp = reqwest::Client::new()
        .get(url)
        .send()
        .await?;

    println!("resp content: {}", resp.text_with_charset("utf8").await?);
    Ok(())
}

总结

学习一门编程语言除了学习这门语言本身的语法和标准库之外使用得最多的可能就是第三方库了, 而http客户端是几乎必学的,由于笔者比较喜欢异步的生态,所以这里用的是reqwest作为学习对象

值得说明的是: reqwest也可以以同步的方式使用。

参考链接

下面是一些本文在写代码时用到的参考连接

  • https://www.makeuseof.com/rust-reqwest-http-requests
  • https://github.com/seanmonstar/reqwest
  • https://docs.rs/reqwest/latest/reqwest/

原文链接: https://youerning.top/post/reqwest-tutorial/

标签:HTTP,url,resp,await,reqwest,let,main,rust
From: https://blog.51cto.com/youerning/8657343

相关文章

  • java使用http工具类调用第三方接口
    java使用http工具类调用第三方接口一、所需maven依赖:<!--json依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.75</version>......
  • 关于解决vue报错"Problems loading reference 'https://schemastore.azurewebsites.ne
    打开setting时会看到有一条三角形的警告信息 看问题描述:无法从该网站加载解决方法:打开设置,找到扩展下的json项 设置之后可以在settings.json文件中看到新增加一项 "json.schemaDownload.enable":false可以直接在界面上设置: "json.schemaDownload.enable":false......
  • rust的musl toolchain
    rust项目常常会使用musl作为编译target,这个时候就会使用musl的工具链。musltoolchain安装在$HOME/.rustup/toolchain下面。通常可以用rustup安装,比如:rustupinstallstable-unknown-linux-musl也可以使用rust官方提供的脚本:curl--proto'=https'--tlsv1.2-sSfhttps://......
  • 【直播协议详解】RTMP、HLS、HTTP-FLV、WebRTC、RTSP的区别
    本期我们详细讨论直播的相关协议,包括:HTTP-FLV、HLS、RTMP、Web-RTC、RTSP等等。我们将会详细介绍这些协议的工作原理、应用场景、及延迟的原因。我们按这样的顺序讨论​:RTMP、HTTP-FLVHLSWeb-RTCRTSP一、RTMP、HTTP-FLV协议RTMP和HTTP-FLV都是建立在FLV封装之上的。RTM......
  • 像使用stl一样使用线段树 ——AtCoder Library(转载https://zhuanlan.zhihu.com/p/459
    地址:https://zhuanlan.zhihu.com/p/459579152 我这里翻译一下官方的文档。首先需要满足几个性质。(注意 ∗ 是个操作,不是单纯的一个乘号)1)操作满足结合律即 (a∗b)∗c=a∗(b∗c)2)操作需要有个幺元(基本元/单位元)a∗e=e∗a=a如果你有这个一个序列S,长度为N ,接下......
  • python HTTP Server 文件上传与下载
    pythonHTTPServer文件上传与下载实现在局域网(同一WIFI下)文件上传与下载该模块通过实现标准GET在BaseHTTPServer上构建和HEAD请求。(将所有代码粘贴到同一个py文件中,即可使用)所需包基于python3版本实现,python2版本无涉猎importosimportsysimportargparseimport......
  • OKHttp的基本又核心的使用,手把手教程
    真就是手把手教你如何使用OKHTTP进行网络请求先说问题,解疑答惑**1.什么是URL什么是URI**URI:统一资源标识符URL:统一资源定位符范围来说URL<URIURL实际上也是一种资源标识符,只不过长得有点像,用来做区分2.HTTP和HTTPS有什么区别没什么区别,可能HTTPS会加密,其他好像没什么区别3.三次......
  • C#中HttpWebRequest发起HTTP请求,如何设置才能达到最大并发和性能
    在C#中使用HttpWebRequest发起HTTP请求时,达到最大并发和性能可以从以下几个方面改进:1.ServicePointManager设置ServicePointManager 类是一个静态类,它提供了用于管理HTTP连接的属性和方法。为了提升并发性能,你需要调整以下几个关键属性:DefaultConnectionLimit:默认情况下,.N......
  • 一个服务开两个端口 一个http 一个https
    新建一个类TomcatServerCustomerConfig实现接口WebServerFactoryCustomizer这些就可以同时有http接口和https接口了。基于springbott+tomcatimportorg.apache.catalina.connector.Connector;importorg.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFa......
  • C#的HttpWebRequest发送form-data数据
    以下是使用C#中的HttpWebRequest发送post请求的示例代码,请求头为form-data,可以上传文件。你可以将它封装成一个通用的方法。1publicstaticstringHttpPost(stringurl,Dictionary<string,string>parameters,Dictionary<string,string>files)2{stringstrBou......