如何从 Rust 发出 HTTP 请求?我似乎在核心库中找不到任何东西。
我不需要解析输出,只需发出请求并检查 HTTP 响应代码。
如果有人可以向我展示如何对我的 URL 上的查询参数进行 URL 编码,则加分!
在 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-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 的单元测试中有一些例子。
我一直在研究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)]
)来完成。适当的转换函数:
extra::url::query_from_str
(抱歉,目前不能使用,因为它是私有的。PR 即将公开。同时,这个链接实际上不应该工作,它只是因为https://github.com/才可用Mozilla/rust/issues/7476 .)
使用 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());
}
我更喜欢依赖计数低的板条箱,所以我会推荐这些:
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(())
}
为了详细说明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 版本进行了更新。
基于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"}
使用超“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(())
}