22-09-23
banq
1、设置新项目:
# create a new Rust binary application cargo new rust-axum-email-server navigate to the project directory cd rust-axum-email-server fire up the server cargo watch -x run |
2、准备依赖包:
在项目的根目录下打开一个新的命令行解释器,然后执行以下命令:
install dependencies cargo add axum tokio -F tokio/full lettre serde -F serde/derive dotenv |
通过参数-F <crate>/<feature>. 下载拉入以下依赖:
- axum,我们的 Web 框架
- tokio的全部功能, Rust 的异步运行时
- lettre crate,Rust 的邮件程序库
- serde的派生特性 ,一个用于解析 JSON 的 crate,
- dotenv用于在开发中解析环境变量
此时,我们的应用程序清单 ( Cargo.toml) 将如下所示:
[package] name = "rust-axum-email-server" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html <p class="indent">[dependencies] axum = "0.5.16" dotenv = "0.15.0" lettre = "0.10.1" serde = { version = "1.0.144", features = ["derive"] } tokio = { version = "1.21.1", features = ["full"] } |
3、编码
切换导航到src/main.rs内容并将其替换为以下列表:
use axum::{response::Html, routing::get, Router}; use std::net::SocketAddr; #[tokio::main] async fn main() { // build our application with a route let app = Router::new().route("/", get(handler)); // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); println!("listening on http://{}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn handler() -> Html<&'static str> { Html("<h1>Hello, World!</h1>") } |
这段是axum hello-word example的摘录。
我们首先从 axum 导入所需的模块以及SocketAddr从 Rust 标准库导入的模块,这SocketAddr是一个 IP 地址构建器。我们正在使用它来构造 localhost IP 地址,如下所示:
let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); |
最后,我们将地址传递给我们的 Axum 服务器实例:
... axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); |
Axum 使用“handler”来表示我们通常在Node 的 Expressjs的controller等框架中类似功能。
控制器(或handler)本质上是接受和解析我们的 HTTP 请求并处理它们以返回 HTTP 响应的函数或方法。
async fn handler() -> Html<&'static str> { Html("<h1>Hello, World!</h1>") } |
我们的服务器已将一个处理程序安装到基本路由,如下所示:
... // build our application with a route let app = Router::new().route("/", get(handler)); ... |
一旦您在浏览器中访问https://127.0.0.1:3000,这个路由就会打印出“Hello World”。
让我们继续创建一个处理程序来发送我们的电子邮件。就在我们这样做之前,我们需要创建一个.env文件来保存我们的环境变量。
create a .env file touch .env |
填充 . env包含以下字段及其对应值的文件:
the SMTP username, typically the full email address SMTP_USER= the SMTP password SMTP_PASSWORD= SMTP host SMTP_HOST= |
现在,让我们到 src/main.rs 创建我们的处理程序,一旦完成,我们将把处理程序挂载到路由中。
要做到这一点,请将 src/main.rs 的内容替换为下面的列表,注意注释和添加的片段:
use axum::{ response::{Html, IntoResponse}, routing::{get, post}, Json, Router, }; use dotenv::dotenv; // import the dotenv crate for parsing the `.env file` use serde::{Deserialize, Serialize}; use std::env; //for getting fields from the environment use std::net::SocketAddr; // import serde for parsing our struct to and from Json //import the email library needed modules use lettre::transport::smtp::authentication::Credentials; use lettre::{Message, SmtpTransport, Transport}; /// define a structure that maps to the format of our HTTP request body /// derive the Debug trait, this will allow, printing the struct in stdout /// derive the Serializing trait, this will allow building up JSON /// derive the Deserializing trait #[derive(Debug, Serialize, Deserialize)] struct EmailPayload { fullname: String, email: String, message: String, } //mount the tokio runtime to allow our main function to support asynchronous execution #[tokio::main] async fn main() { dotenv().ok(); // build our application with a route let app = Router::new() .route("/", get(handler)) //mount the handle to a path, using the HTTP POST verb .route("/send-email", post(dispatch_email)); // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); println!("listening on http://{}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn handler() -> Html<&'static str> { Html("<h1>Hello, World!</h1>") } /// define an email handler, the handler will take the user's email address and the message body /// the parsed payload will be fed into the `lettre` library and finally, a response will be returned async fn dispatch_email(Json(payload): Json<EmailPayload>) -> impl IntoResponse { // println!("{:#?}", payload); //destructure the HTTP request body let EmailPayload { email, message, fullname, } = &payload; //contruct emil config let from_address = String::from("You <you@yordomain.com>"); let to_address = format!("{fullname} <{email}>"); let reply_to = String::from("You <you@yordomain.com>"); let email_subject = "Axum Rust tutorial"; let email = Message::builder() .from(from_address.parse().unwrap()) .reply_to(reply_to.parse().unwrap()) .to(to_address.parse().unwrap()) .subject(email_subject) .body(String::from(message)) .unwrap(); let creds = Credentials::new( env::var("SMTP_USERNAME").expect("SMTP Username not specified "), env::var("SMTP_PASSWORD").expect("SMTP Password not specified"), ); // Open a remote connection to SMTP server let mailer = SmtpTransport::relay(&env::var("SMTP_HOST").expect("SMTP Host not specified")) .unwrap() .credentials(creds) .build(); // Send the email match mailer.send(&email) { Ok(_) => println!("Email sent successfully!"), Err(e) => panic!("Could not send email: {:?}", e), } } |
解释
- 我首先从每个 crate 导入所需的模块
... use dotenv::dotenv; // import the dotenv crate for parsing the `.env file` use serde::{Deserialize, Serialize}; use std::env; //for getting fields from the environment use std::net::SocketAddr; // import serde for parsing our struct to and from Json //import the email library needed modules use lettre::transport::smtp::authentication::Credentials; use lettre::{Message, SmtpTransport, Transport}; ... |
- 我定义了一个包含 HTTP 有效负载的数据结构,这是Axum Extractor所要求的,我将在以下部分中讨论它。
... #[derive(Debug, Serialize, Deserialize)] struct EmailPayload { fullname: String, email: String, message: String, } ... |
- 我初始化了dotenv crate以允许在开发中解析环境变量,然后我使用Axum的post方法将路由处理程序安装到/send-email路由上,处理服务器接受的请求。
... dotenv().ok(); // build our application with a route let app = Router::new() .route("/", get(handler)) //mount the handle to a path, using the HTTP POST verb .route("/send-email", post(dispatch_email)); ... |
总结部分基本上是,插入我们的环境变量,解析我们的 HTTP 请求负载并发送电子邮件,并在控制台中打印响应。
测试
对于测试,您可以使用任何您熟悉的 HTTP 客户端,最常见的是Curl和 Postman。
但是,我发现Thunder Client使用起来更方便,因为它是一个 VS Code 扩展。这意味着我可以在舒适的情况下做任何事情。