88

如何从 Rust 发出 HTTP 请求?我似乎在核心库中找不到任何东西。

我不需要解析输出,只需发出请求并检查 HTTP 响应代码。

如果有人可以向我展示如何对我的 URL 上的查询参数进行 URL 编码,则加分!

4

8 回答 8

92

在 Rust 中发出 HTTP 请求的最简单方法是使用reqwest crate:

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let resp = reqwest::blocking::get("https://httpbin.org/ip")?.text()?;
    println!("{:#?}", resp);
    Ok(())
}

Cargo.toml

[dependencies]
reqwest = { version = "0.11", features = ["blocking"] }

异步

Reqwest 还支持使用Tokio发出异步HTTP 请求:

use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let resp = reqwest::get("https://httpbin.org/ip")
        .await?
        .text()
        .await?;
    println!("{:#?}", resp);
    Ok(())
}

Cargo.toml

[dependencies]
reqwest = "0.11"
tokio = { version = "1", features = ["full"] }

Reqwest 是一个易于使用的Hyper包装器,Hyper是一个流行的 Rust 的 HTTP 库。如果您需要对管理连接进行更多控制,您可以直接使用它。下面是一个基于Hyper的示例,其灵感主要来自其文档中的示例

use hyper::{body::HttpBody as _, Client, Uri};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let client = Client::new();

    let res = client
        .get(Uri::from_static("http://httpbin.org/ip"))
        .await?;

    println!("status: {}", res.status());

    let buf = hyper::body::to_bytes(res).await?;

    println!("body: {:?}", buf);
}

Cargo.toml

[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }

原始答案(Rust 0.6)

我相信您正在寻找的是标准库。现在在rust-http和 Chris Morgan 的答案是在可预见的未来当前 Rust 的标准方式。我不确定我能带你走多远(希望我不会带你走错方向!),但你会想要这样的东西:

// Rust 0.6 -- old code
extern mod std;

use std::net_ip;
use std::uv;

fn main() {
    let iotask = uv::global_loop::get();
    let result = net_ip::get_addr("www.duckduckgo.com", &iotask);

    io::println(fmt!("%?", result));
}

至于编码,在 src/libstd/net_url.rs 的单元测试中有一些例子。

于 2013-01-07T02:30:01.750 回答
31

更新:这个答案指的是相当古老的历史。对于当前的最佳实践,请查看Isaac Aggrey 的答案


我一直在研究rust-http,它已经成为 Rust 的事实上的HTTP 库(Servo 使用它);目前它还远未完成,而且文档记录也很差。这是一个发出请求并使用状态码执行某些操作的示例:

extern mod http;
use http::client::RequestWriter;
use http::method::Get;
use http::status;
use std::os;

fn main() {
    let request = RequestWriter::new(Get, FromStr::from_str(os::args()[1]).unwrap());
    let response = match request.read_response() {
        Ok(response) => response,
        Err(_request) => unreachable!(), // Uncaught condition will have failed first
    };
    if response.status == status::Ok {
        println!("Oh goodie, I got me a 200 OK response!");
    } else {
        println!("That URL ain't returning 200 OK, it returned {} instead", response.status);
    }
}

使用 URL 作为唯一的命令行参数运行此代码,它将检查状态代码!(仅限 HTTP;无 HTTPS。)

比较src/examples/client/client.rs一个做得更多的例子。

rust-http 正在跟踪 rust 的 master 分支。目前它可以在刚刚发布的 Rust 0.8 中工作,但很快就会有重大变化。 实际上,没有任何版本的 rust-http 可以在 Rust 0.8 上运行——在发布之前,隐私规则中存在一个无法解决的重大变化,导致 rust-http 依赖于 extra::url 的某些内容无法访问。这已被修复,但它使 rust-http 与 Rust 0.8 不兼容。


至于查询字符串编码的事情,目前应该用extra::url::Query(a typedef for ~[(~str, ~str)])来完成。适当的转换函数:

于 2013-09-27T14:01:02.620 回答
22

使用 curl 绑定。把它贴在你的Cargo.toml

[dependencies.curl]
git = "https://github.com/carllerche/curl-rust"

...这在src/main.rs

extern crate curl;

use curl::http;

fn main(){
  let resp = http::handle()
    .post("http://localhost:3000/login", "username=dude&password=sikrit")
    .exec().unwrap();

  println!("code={}; headers={}; body={}",
    resp.get_code(), resp.get_headers(), resp.get_body());    

}
于 2014-10-06T09:06:51.297 回答
6

我更喜欢依赖计数低的板条箱,所以我会推荐这些:

MinReq(0 次)

use minreq;

fn main() -> Result<(), minreq::Error> {
   let o = minreq::get("https://speedtest.lax.hivelocity.net").send()?;
   let s = o.as_str()?;
   print!("{}", s);
   Ok(())
}

HTTP_Req (35 次)

use {http_req::error, http_req::request, std::io, std::io::Write};

fn main() -> Result<(), error::Error> {
   let mut a = Vec::new();
   request::get("https://speedtest.lax.hivelocity.net", &mut a)?;
   io::stdout().write(&a)?;
   Ok(())
}
于 2020-12-01T03:15:12.577 回答
4

为了详细说明Isaac Aggrey 的答案,这里有一个使用reqwest库发出带有查询参数的 POST 请求的示例。

货运.toml

[package]
name = "play_async"
version = "0.1.0"
edition = "2018"

[dependencies]
reqwest = "0.10.4"
tokio = { version = "0.2.21", features = ["macros"] }

代码

use reqwest::Client;

type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;

async fn post_greeting() -> Result<()> {
    let client = Client::new();
    let req = client
        // or use .post, etc.
        .get("https://webhook.site/1dff66fd-07ff-4cb5-9a77-681efe863747")
        .header("Accepts", "application/json")
        .query(&[("hello", "1"), ("world", "ABCD")]);

    let res = req.send().await?;
    println!("{}", res.status());

    let body = res.bytes().await?;

    let v = body.to_vec();
    let s = String::from_utf8_lossy(&v);
    println!("response: {} ", s);

    Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
    post_greeting().await?;

    Ok(())
}

转到https://webhook.site并创建您的 webhook 链接并更改代码以匹配。您将看到服务器上实时收到请求。

此示例最初基于Bastian Gruber 的示例,并已针对现代 Rust 语法和更新的 crate 版本进行了更新。

于 2019-09-03T11:23:08.483 回答
2

基于Patrik Stas 的回答,如果您想做一个 HTTP 表单 URL 编码的 POST,这就是您必须做的。在这种情况下,它是获取 OAuthclient_credentials令牌。

货运.toml

[dependencies]
reqwest = "0.10.4"
tokio = { version = "0.2.21", features = ["macros"] }

代码

use reqwest::{Client, Method};

type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;

async fn print_access_token() -> Result<()> {
    let client = Client::new();
    let host = "login.microsoftonline.com";
    let tenant = "TENANT";
    let client_id = "CLIENT_ID";
    let client_secret = "CLIENT_SECRET";
    let scope = "https://graph.microsoft.com/.default";
    let grant_type = "client_credentials";

    let url_string = format!("https://{}/{}/oauth2/v2.0/token", host, tenant);
    let body = format!(
        "client_id={}&client_secret={}&scope={}&grant_type={}",
        client_id, client_secret, scope, grant_type,
    );
    let req = client.request(Method::POST, &url_string).body(body);

    let res = req.send().await?;
    println!("{}", res.status());

    let body = res.bytes().await?;

    let v = body.to_vec();
    let s = String::from_utf8_lossy(&v);
    println!("response: {} ", s);

    Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
    print_access_token().await?;

    Ok(())
}

这将打印如下内容。

200 OK
response: {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"ACCESS_TOKEN"} 
于 2020-05-27T14:21:40.510 回答
1

使用“0.13”

还使用hyper-tls支持 HTTPS。

文件Cargo.toml

hyper = "0.13"
hyper-tls = "0.4.1"
tokio = { version = "0.2", features = ["full"] }

代码

extern crate hyper;
use hyper::Client;
use hyper::body::HttpBody as _;
use tokio::io::{stdout, AsyncWriteExt as _};
use hyper_tls::HttpsConnector;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // HTTP only
    // let client = Client::new();

    // http or https connections
    let client = Client::builder().build::<_, hyper::Body>(HttpsConnector::new());

    let mut resp = client.get("https://catfact.ninja/fact".parse()?).await?;

    println!("Response: {}", resp.status());

    while let Some(chunk) = resp.body_mut().data().await {
        stdout().write_all(&chunk?).await?;
    }

    Ok(())
}

改编自https://hyper.rs/guides/client/basic/

于 2020-02-03T21:34:51.410 回答
1

在此处删除使用surf板条箱的版本(与板条箱双重tide):

let res = surf::get("https://httpbin.org/get").await?;
assert_eq!(res.status(), 200);
于 2021-03-08T20:26:26.777 回答