BEGINNING RUST PROGRAMMING
Author: Ric Messier
如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的
Chapter 10 Web Communications
CLIENT COMMUNICATION
use hyper::{Client, body::HttpBody as _};
use hyper_tls::HttpsConnector;
use tokio::io::{AsyncWriteExt as _};
use tokio::fs::File;
use std::env;
use voca_rs::*;
async fn write_to_file(data: &String) -> Result<(), Box<dyn std::error::Error>> {
let mut output_file = File::create("resp-output.txt").await?;
output_file.write_all(data.as_bytes()).await?;
Ok(())
}
fn print_to_screen(data: &String) {
let stripped = strip::strip_tags(data);
let clean = stripped.replace("\n\n", "");
println!("{}", clean);
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let https = HttpsConnector::new();
let client = Client::builder().build::<_, hyper::Body>(https);
let mut hostname = String::new();
let mut write_file = false;
let mut print_file = false;
let mut body = String::new();
let args: Vec<String> = env::args().collect();
for position in 1..(args.len()) {
if position == args.len() - 1 {
hostname = String::from(&args[position]);
}
match args[position].as_str() {
"-w" => write_file = true,
"-p" => print_file = true,
_ => ()
}
}
let mut res = client.get(hostname.parse()?).await?;
println!("Headers:\n{:#?}", res.headers());
while let Some(chunk) = res.body_mut().data().await {
let chunk = chunk?;
body.push_str(&(String::from_utf8_lossy(&chunk)));
}
if write_file {
write_to_file(&body).await?;
}
if print_file {
print_to_screen(&body);
}
Ok(())
}
[dependencies]
hyper = "*"
tokio = { version = "*", features = ["macros", "tcp", "fs"] }
hyper-tls = "*"
voca_rs = "*"
如果您引入了很多外部功能,那么在某种程度上,您可能会遇到与符号的冲突,这意味着某个东西的命名方式。这在编写我们在这里讨论的程序的过程中是正确的。稍后我们将讨论这发生的地方,但是在程序中使用的两个库之间存在名称冲突。这导致了简单地找到另一种方法,而不是纠正名称空间。解决名称空间重叠问题的一种方法是在不导入名称空间符号的情况下导入trait,这意味着您没有引入trait所在的桶的名称;你只是引入了trait。
use hyper::{Client, body::HttpBody as _};
当我们写成_的时候,我们所说的话和我们过去在使用下划线时说过的话是一样的,尽管它可能并不总是很清楚。强调说这并不重要。当您编译一个Rust程序时,您在标识符中存储一个值,但不使用标识符,编译器说用_启动标识符,以表明这并不重要,尽管这不是编译器具体说的。在我们写程序的时候,有很多地方我们做的事情只是因为我们必须这样做,而不是因为我们关心结果,这意味着我们在寻找副作用,结果可能是无关紧要的。当我们导入as _时,这意味着符号或名称空间名称在这里不重要。只要引入这些特征就可以使用,因为我们可能与符号名称有冲突。
就正在使用的其他库而言,它们中的大多数都与能够处理HTTP请求所必需的功能有关,而不需要自己创建该功能,因为从实现的角度来看,HTTP并不是一个简单的协议。这将是大量的工作,而且由于其他人已经完成了,并且实现已经被很多人测试和使用,它可能比我们能做的任何事情都更可靠,当然也更经过战斗考验。稍后您将看到的是,我们引入的功能引入了异步通信。这就是为什么我们必须使用tokio的文件功能——因为一旦您开始启动异步通信路径,一切都必须是异步的,以确保您在执行过程中获得安全性
Jumping Ahead
先解释main方法,首先是#[tokio::main]
,这是来自Tokio 库,它告诉编译器主函数需要修正。我们要按照我们通常运行主函数的方式来写主函数。当编译器运行时,tokio将取代我们写的,或者说它会把我们写到另一个函数,把自己的主要函数,创建一个多线程的方法,启动不同的线程,维护异步性质的通信发生。每次调用一个可能是异步的特性时,它都需要在一个单独的线程中,而tokio就会为我们管理所有这些特性。
您还将注意到主函数有一个额外的符号,表明它是异步的。这要追溯到tokio为我们所做的事情。我们将以一种异步的方式使用远程服务器,所以主函数需要知道,它不能在所有线程都完成之前直接退出。
let https = HttpsConnector::new();
let client = Client::builder().build::<_, hyper::Body>(https);
我们需要提供一些额外的细节,以获得正确的客户端回来。我们需要指示一个连接器,连接器指示目的地。因为目的地将在以后得到处理,所以我们提供了一个_作为一种提供无关或不必要的值的方法。我们还需要指出有效载荷。
let mut res = client.get(desturl.parse()?).await?;
这是一个异步请求;我们正在使用await来指示我们将等待结果,而不需要提供对另一个函数的回调来处理来自这个调用的返回。同样,我们有一个错误可能需要处理或展开值的情况,所以我们正在使用?来处理这两种可能性。
说到Futures,本质上是占位符。当我们执行一项任务时,该任务可能会启动并做一些工作,但我们仍然需要有一种前进的方法,因为我们是异步的。在异步编程中,您可以做一种叫做polling的事情。这意味着我们会继续回去检查那些被发送去做一些工作的过程。一旦任务完成,我们就会得到任务的任何结果。同时,我们需要一个占位符,直到我们得到最终的结果。这就是Futures的意义所在。它们是一种启动任务的方式,同时仍然有一种方式来推进需要发生的任务,而这些任务可能不依赖于这个结果。
Chapter 11 Web Server
API Server in Rust
使用的是warp
库,它是建立在hyper
之上的,因为hyper
使用了tokio
,所以这里也使用了
use warp::Filter;
use std::fs;
#[tokio::main]
async fn main() {
let bacon_contents = fs::read_to_string("bacon.txt")
.expect("Unable to open bacon.txt file");
let bacon = warp::path("bacon").map(move || format!("{}", &bacon_contents));
warp::serve(bacon)
.run(([127, 0, 0, 1], 8080))
.await;
}
use warp::Filter;
use std::fs;
#[tokio::main]
async fn main() {
let bacon_contents = fs::read_to_string("bacon.txt")
.expect("Unable to open bacon.txt file");
let bacon = warp::path("bacon").map(move || format!("{}", &bacon_contents));
let hello = warp::path!("hello" / "you").map(|| "Hello, you\n");
let bye = warp::path("bye")
.and(warp::path::param())
.map(|name: String| format!("Goodbye, {}!\n", name));
let routes = warp::get().and(bacon.or(hello).or(bye));
warp::serve(routes)
.run(([127, 0, 0, 1], 8080))
.await;
}
如果是多个端点的形式,需要将它们连在一起,所以就有了
let routes = warp::get().and(bacon.or(hello).or(bye));
不过看起来很奇怪的语法
RUST ROCKET
Rocket框架,书上说要使用nightly,所以就没有试验
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
use rocket::Data;
use std::fs;
#[get("/")]
fn index() -> &'static str {
"Welcome to your very own server, but there is nothing at the main branch\n"
}
#[get("/bacon")]
fn bacon() -> String {
let bacon_contents = fs::read_to_string("bacon.txt")
.expect("Unable to open file");
format!("{}\n", bacon_contents)
}
#[post("/upload", format = "plain", data = "<data>")]
fn upload(data: Data) -> Result<String, std::io::Error> {
data.stream_to_file("/tmp/data.txt").map(|num_bytes| format!("Wrote {} bytes
out to file", num_bytes))
}
#[get("/greetz/<name>/<age>")]
fn greetz(name: String, age: u8) -> String {
format!("Greetz, {} year old named {}!", age, name)
}
#[get("/ofage/<name>/<age>")]
fn ofage(name: String, age: u8) -> String {
if age > 18 {
format!("Welcome, {}, your age {} means you can view this content!", name, age)
}
else {
format!("Sorry, {}, you are not the right age, since you are only {} years
old", name, age)
}
}
fn main() {
rocket::ignite().mount("/", routes![index,greetz,upload,bacon,ofage])
.launch();
}
标签:use,PROGRAMMING,bacon,warp,---,let,file,tokio,随记
From: https://www.cnblogs.com/huangwenhao1024/p/16927132.html