如何从Rust发出HTTP请求?

时间:2013-01-04 09:50:33

标签: http rust

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

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

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

8 个答案:

答案 0 :(得分:42)

在Rust中执行HTTP的最简单方法是reqwest。它是使Hyper更易于使用的包装器。

Hyper是Rust的一个流行的HTTP库,它使用两个库:Tokio的事件循环来进行非阻塞请求,futures-rs用于期货/承诺。基于Hyper的示例如下,主要受an example in its documentation启发。

// Rust 1.19, Hyper 0.11, tokio-core 0.1, futures 0.1

extern crate futures;
extern crate hyper;
extern crate tokio_core;

use futures::{Future};
use hyper::{Client, Uri};
use tokio_core::reactor::Core;

fn main() {
    // Core is the Tokio event loop used for making a non-blocking request
    let mut core = Core::new().unwrap();

    let client = Client::new(&core.handle());

    let url : Uri = "http://httpbin.org/response-headers?foo=bar".parse().unwrap();
    assert_eq!(url.query(), Some("foo=bar"));

    let request = client.get(url)
        .map(|res| {
            assert_eq!(res.status(), hyper::Ok);
        });

    // request is a Future, futures are lazy, so must explicitly run
    core.run(request).unwrap();
}

Cargo.toml

[dependencies]
hyper = "0.11"
tokio-core = "0.1"
futures = "0.1"

对于后人,我在下面留下了我的原始答案,但请参见上面的Rust 1.19更新(最新的稳定版本在撰写本文时)。

我相信你所寻找的是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中的单元测试中有一些例子。

答案 1 :(得分:21)

更新:这个答案指的是相当古老的历史。对于当前的最佳做法,请改为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 0.8中运行,但很快就会有很多变化。 实际上,没有版本的rust-http可以在Rust 0.8上运行 - 有一个在发布之前的隐私规则中无法解决的更改,留下了一些生锈的http取决于extra :: url不可访问。这已被修复,但它留下锈-http与Rust 0.8不兼容。


对于查询字符串编码问题,目前应该使用extra::url::Query~[(~str, ~str)]的typedef)来完成。转换的适当功能:

答案 2 :(得分:13)

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

}

答案 3 :(得分:1)

要详细说明艾萨克的答案,下面是使用reqwest库通过查询参数发出POST请求的示例。

Cargo.toml

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

[dependencies]
reqwest = "0.9.14"
tokio = "0.1.18"
futures = "0.1.21"

代码

use futures::{Future, Stream};
use reqwest::r#async::{Client, Decoder};
use std::mem;

fn post_greeting() -> impl Future<Item=(), Error=()> {
    Client::new()
        .get("https://webhook.site/1dff66fd-07ff-4cb5-9a77-681efe863747")
        .header("Accepts", "application/json")
        .query(&[
            ("hello", "1"),
            ("world", "ABCD"),
        ])
        .send()
        .and_then(|mut res| {
            println!("{}", res.status());
            let body = mem::replace(res.body_mut(), Decoder::empty());
            body.concat2()
        })
        .map_err(|err| println!("request error: {}", err))
        .map(|body| {
            let v = body.to_vec();
            let s = String::from_utf8_lossy(&v);
            println!("response: {} ", s);
        })
}

fn main() {
    let f = post_greeting();
    tokio::run(f);
}

继续进行https://webhook.site并创建您的Webhook链接并更改代码。您将实时看到服务器上收到的请求。

主要基于此处的巴斯蒂安·格鲁伯(Bastian Gruber)的示例https://github.com/gruberb/futures_playground

答案 4 :(得分:1)

我更喜欢依赖计数较低的板条箱,因此我建议这些:

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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 :(得分:0)

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

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