【问题标题】:How do I make an HTTP request from Rust?如何从 Rust 发出 HTTP 请求?
【发布时间】:2013-01-04 09:50:32
【问题描述】:

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

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

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

【问题讨论】:

  • @alex-dean,您能否在 2017 年将这个问题的标记答案更改为与现实相符。谢谢。
  • 我想删除我的答案,但我不能,因为它已被接受。请更改接受的答案。

标签: http rust


【解决方案1】:

在 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 包装器,它是 Rust 的流行 HTTP 库。如果您需要对管理连接进行更多控制,您可以直接使用它。下面是一个基于Hyper 的示例,其灵感主要来自an example in its documentation

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)

我相信您正在寻找的是standard library。现在在 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的单元测试中有一些例子。

【讨论】:

【解决方案2】:

更新:这个答案指的是相当古老的历史。有关当前的最佳做法,请查看 Isaac Aggrey's answer


我一直在研究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~[(~str, ~str)]的typedef)来完成。转换的适当函数:

【讨论】:

  • 如何正确编译?现在我把它放在 rust-http/src/examples/client2/main.rs 并运行(取自'make examples'输出) rustc -O -Z debug-info src/examples/client2/main.rs -o build /examples/client2 -L 构建/
  • @rofrol: rust-http 当前会生成一对作为预编译步骤,因此您还不能通过 rustpkg(否则为 rustpkg install github.com/chris-morgan/rust-http)方便地安装它。重要的是,当你编译它时,它必须能够找到 libhttp-*.so;这就是-L build/ 的用途,因为该文件位于build 目录中。所以对于你的代码,它可以是-L /path/to/rust-http/build/,或者你可以将它复制到你的构建目录中,或者类似的东西。别担心,到 Rust 0.9 倍,情况应该会好一些。
  • 请注意,rust-http 现在被标记为已过时。它的作者推荐 Hyper 而不是 github.com/hyperium/hyper
  • 嘿,现在它被 hyper 取代了,这也没有很好的记录:>
  • @CodyGray:考古学家是唯一能发现它有用的人。答案本身在三年左右的时间里完全不相关;所涉及的代码不会运行,并且 rust-http 本身需要一些相当侵入性的更改才能运行,因为这是在 Rust 稳定 (1.0) 之前的一段时间。
【解决方案3】:

使用 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());    

}

【讨论】:

  • 一种快速且低摩擦的方法 - 不错!
  • 这个答案建议使用 C 来做 HTTP,但纯 Rust 解决方案更合适,因为它们更易于团队维护,也因为 Rust 比 C 更安全。
  • @JeffAllen 可能更合适,但几乎不可能找到。所以我选择了一个次优的,它适用于一个“适当的”,而不是。
  • 这将比其他答案好得多,因为它避免了处理“期货”,这是我见过的最复杂、无文档的事情之一。太糟糕了,因为没有可用的curl::http(不再),所以它不起作用——只有一种叫做“简单”的东西,它本身就很复杂,几乎没有记录。
【解决方案4】:

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

MinReq (0 deps)

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(())
}

【讨论】:

  • 刚刚使用过 minreq,可以说它真的很好用。请注意,如果您需要 HTTPS 支持,它有 7 个依赖项。
【解决方案5】:

为了详细说明Isaac Aggrey's answer,下面是一个使用reqwest 库发出带有查询参数的POST 请求的示例。

Cargo.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's example,并已针对现代 Rust 语法和更新的 crate 版本进行了更新。

【讨论】:

  • 您有使用 POST 的示例吗? HTTP 表单允许 GET 和 POST,但这是使用 GET。
  • @tom 将.get 替换为.post
  • 这似乎还不够。我的请求需要使用 Content-Type "application/x-www-form-urlencoded" 发送。我尝试用它添加一个header() 电话,我回来了HTTP Error 411. The request must be chunked or have a content length.
【解决方案6】:

Patrik Stas' answer 的基础上,如果您想做一个HTTP 表单URL 编码的POST,您必须这样做。在这种情况下,它是获取 OAuth client_credentials 令牌。

Cargo.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"} 

【讨论】:

    【解决方案7】:

    在此处删除使用surf crate 的版本(与tide crate 相同):

    let res = surf::get("https://httpbin.org/get").await?;
    assert_eq!(res.status(), 200);
    

    【讨论】:

      【解决方案8】:

      使用hyper“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/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-20
        • 2017-03-09
        • 2011-10-04
        • 2018-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多