如何从Rust发出HTTP请求?我似乎无法在核心库中找到任何东西。
我不需要解析输出,只需发出请求并检查HTTP响应代码。
如果有人可以告诉我如何对我的网址上的查询参数进行网址编码,则会有加分标记!
答案 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)
我一直在研究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)来完成。转换的适当功能:
extra::url::query_from_str
(对不起,目前不能使用它,因为它是私有的。公关即将公开。同时,这个链接实际上不应该工作,它只是因https://github.com/mozilla/rust/issues/7476而可用。)
答案 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)
我更喜欢依赖计数较低的板条箱,因此我建议这些:
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(())
}
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(())
}
答案 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)