发送新闻邮件
是时候把邮件发出去了!
我们可以利用前几章写的 EmailClient —— 就像 PgPool 一样,它已经是应用程序状态的一部分了, 我们可以使用 web::Data 来提取它。
//! src/routes/newsletters.rs
// [...]
pub async fn publish_newsletter(
body: web::Json<BodyData>,
pool: web::Data<PgPool>,
// New argument!
email_client: web::Data<EmailClient>,
) -> Result<HttpResponse, PublishError> {
let subscribers = get_confirmed_subscribers(&pool).await?;
for subscriber in subscribers {
email_client
.send_email(
subscriber.email,
&body.title,
&body.content.html,
&body.content.text,
)
.await?;
}
Ok(HttpResponse::Ok().finish())
}
差一点就能工作:
error[E0308]: mismatched types
--> src/routes/newsletters.rs:52:17
|
51 | .send_email(
| ---------- arguments to this method are incorrect
52 | subscriber.email,
| ^^^^^^^^^^^^^^^^ expected `SubscriberEmail`, found `String`
|
error[E0277]: `?` couldn't convert the error to `PublishError`
--> src/routes/newsletters.rs:57:19
|
50 | / email_client
51 | | .send_email(
52 | | subscriber.email,
53 | | &body.title,
... |
57 | | .await?;
| | -^ unsatisfied trait bound
| |__________________|
| this can't be annotated with `?` because it has type `Re
sult<_, reqwest::Error>`
context Vs with_context
我们可以快速修复第二个错误
//! src/routes/newsletters.rs
// [...]
// Bring anyhow's extension trait into scope!
use anyhow::Context;
pub async fn publish_newsletter(
body: web::Json<BodyData>,
pool: web::Data<PgPool>,
email_client: web::Data<EmailClient>,
) -> Result<HttpResponse, PublishError> {
let subscribers = get_confirmed_subscribers(&pool).await?;
for subscriber in subscribers {
email_client
.send_email(/* */)
.await
.with_context(|| {
format!("Failed to send newsletter issue to {}", subscriber.email)
})?;
}
Ok(HttpResponse::Ok().finish())
}
// [...]
我们正在使用一个新方法, with_context。
它与 context 密切相关,后者是我们在第 8 章中广泛使用的方法,用于将 Result 的错误版本转换为 anyhow::Error, 同时用上下文信息丰富它。
两者之间有一个关键区别: with_context 是惰性的。
它接受一个闭包作为参数,并且只有在发生错误时才会调用该闭包。
如果您添加的上下文是静态的 - 例如 context("Oh no!") - 它们等效。
如果您添加的上下文有运行时开销,请使用 with_context - 这样可以避免在易出错的操作成功时为错误路径付出代价。
让我们以我们的情况为例: format! 在堆上分配内存来存储其输出字符串。使用 context, 我们每次发送电子邮件时都会分配该字符串。
而使用 with_context, 我们只有在电子邮件发送失败时才会调用 format!。