邮件发送器 (Mailers)

邮件发送器 (Mailer) 将会使用现有的 loco 后台 worker 基础设施在后台发送邮件。 这对你来说将是完全无缝的。

发送邮件

要使用现有的邮件发送器 (Mailer),主要在你的控制器 (Controller) 中:

use crate::{
    mailers::auth::AuthMailer,
}

// in your controllers/auth.rs
// 在你的 controllers/auth.rs 中
async fn register(
    State(ctx): State<AppContext>,
    Json(params): Json<RegisterParams>,
) -> Result<Response> {
    // .. register a user ..
    // .. 注册用户 ..
    AuthMailer::send_welcome(&ctx, &user.email).await.unwrap();
}

这将把邮件发送任务加入队列。操作是即时的,因为发送将在稍后在后台执行。

配置

邮件发送器 (Mailer) 的配置在 config/[stage].yaml 文件中完成。 这是默认配置:

# Mailer Configuration.
# 邮件发送器配置。
mailer:
  # SMTP mailer configuration.
  # SMTP 邮件发送器配置。
  smtp:
    # Enable/Disable smtp mailer.
    # 启用/禁用 SMTP 邮件发送器。
    enable: true
    # SMTP server host. e.x localhost, smtp.gmail.com
    # SMTP 服务器主机。例如 localhost, smtp.gmail.com
    host: {{ get_env(name="MAILER_HOST", default="localhost") }}
    # SMTP server port
    # SMTP 服务器端口
    port: 1025
    # Use secure connection (SSL/TLS).
    # 使用安全连接 (SSL/TLS)。
    secure: false
    # auth:
    #   user:
    #   password:

邮件发送器 (Mailer) 通过向 SMTP 服务器发送电子邮件来完成。以下是使用 sendgrid 的示例配置(选择 SMTP 中继选项):

# Mailer Configuration.
# 邮件发送器配置。
mailer:
  # SMTP mailer configuration.
  # SMTP 邮件发送器配置。
  smtp:
    # Enable/Disable smtp mailer.
    # 启用/禁用 SMTP 邮件发送器。
    enable: true
    # SMTP server host. e.x localhost, smtp.gmail.com
    # SMTP 服务器主机。例如 localhost, smtp.gmail.com
    host: {{ get_env(name="MAILER_HOST", default="smtp.sendgrid.net") }}
    # SMTP server port
    # SMTP 服务器端口
    port: 587
    # Use secure connection (SSL/TLS).
    # 使用安全连接 (SSL/TLS)。
    secure: true
    auth:
      user: "apikey"
      password: "your-sendgrid-api-key"

默认邮件地址

除了为每个邮件发送任务指定电子邮件地址外,你可以为每个邮件发送器 (Mailer) 覆盖默认电子邮件地址。

首先,在 Mailer trait 中覆盖 opts 函数,在本例中是针对 AuthMailer

impl Mailer for AuthMailer {
    fn opts() -> MailerOpts {
        MailerOpts {
            from: // set your from email,
                  // 设置你的发件人邮箱地址,
            ..Default::default()
        }
    }
}

在开发中使用邮件捕获器 (mail catcher)

你可以使用像 MailHogmailtutan (用 Rust 编写)这样的应用:

$ cargo install mailtutan
$ mailtutan
listening on smtp://0.0.0.0:1025
listening on http://0.0.0.0:1080

这将启动一个本地 SMTP 服务器和一个友好的 UI,地址为 http://localhost:1080,它可以“捕获”并显示收到的电子邮件。

然后将此内容放入你的 development.yaml 中:

# Mailer Configuration.
# 邮件发送器配置。
mailer:
  # SMTP mailer configuration.
  # SMTP 邮件发送器配置。
  smtp:
    # Enable/Disable smtp mailer.
    # 启用/禁用 SMTP 邮件发送器。
    enable: true
    # SMTP server host. e.x localhost, smtp.gmail.com
    # SMTP 服务器主机。例如 localhost, smtp.gmail.com
    host: localhost
    # SMTP server port
    # SMTP 服务器端口
    port: 1025
    # Use secure connection (SSL/TLS).
    # 使用安全连接 (SSL/TLS)。
    secure: false

现在你的邮件发送器 (mailer worker) worker 将向 localhost 的 SMTP 服务器发送电子邮件。

添加邮件发送器 (mailer)

你可以生成一个邮件发送器 (mailer):

cargo loco generate mailer <mailer name>

或者,如果你想了解其工作原理,可以手动定义它。 在 mailers/auth.rs 中,添加:

static welcome: Dir<'_> = include_dir!("src/mailers/auth/welcome");
impl AuthMailer {
    /// Sending welcome email the the given user
    /// 向给定用户发送欢迎邮件
    ///
    /// # Errors
    /// # 错误
    ///
    /// When email sending is failed
    /// 当邮件发送失败时
    pub async fn send_welcome(ctx: &AppContext, _user_id: &str) -> Result<()> {
        Self::mail_template(
            ctx,
            &welcome,
            Args {
                to: "foo@example.com".to_string(),
                locals: json!({
                  "name": "joe"
                }),
                ..Default::default()
            },
        )
        .await?;
        Ok(())
    }
}

每个邮件发送器 (mailer) 都有一个固定的、预定义的文件夹结构:

src/
  mailers/
    auth/
      welcome/      <-- all the parts of an email, all templates
      // <-- 邮件的所有部分,所有模板
        subject.t
        html.t
        text.t
    auth.rs         <-- mailer definition
    // <-- 邮件发送器定义

运行邮件发送器 (mailer)

邮件发送器 (Mailer) 作为后台 worker 运行,这意味着你需要单独运行 worker 以处理任务 (jobs)。 默认启动命令 cargo loco start 不会启动 worker,因此你需要单独运行它:

要运行 worker,请使用以下命令:

cargo loco start --worker

要同时运行服务器和 worker,请使用以下命令:

cargo loco start --server-and-worker

测试

测试作为你的工作流程一部分发送的电子邮件可能是一项复杂的任务,需要验证各种场景,例如用户注册期间的电子邮件验证和检查用户密码电子邮件。 主要目标是通过检查工作流程中发送的电子邮件数量、查看电子邮件内容以及允许数据快照来简化测试过程。

Loco 中,我们引入了 stub 测试电子邮件功能。 本质上,电子邮件实际上并没有发送; 相反,我们收集关于电子邮件数量及其内容的信息作为测试上下文的一部分。

配置

要在你的测试中启用 stub,请在 YAML 文件中邮件发送器 (mailer) 部分下添加以下字段:

mailer:
  stub: true

注意:如果你的电子邮件发送器在 worker 进程中运行,请确保 worker 模式设置为 ForegroundBlocking。

配置 stub 后,继续进行单元测试 (unit tests) 并按照以下示例操作:

编写测试

测试描述:

  • 创建一个 HTTP 请求到负责发送电子邮件的端点 (endpoint),作为你代码的一部分。
  • 从上下文 (context) 中检索邮件发送器 (mailer) 实例,并调用 deliveries() 函数,该函数包含有关已发送电子邮件数量及其内容的信息。
use loco_rs::testing::prelude::*;

#[tokio::test]
#[serial]
async fn can_register() {
    configure_insta!();

    request::<App, Migrator, _, _>(|request, ctx| async move {
        // Create a request for user registration.
        // 创建一个用户注册请求。

        // Now you can call the context mailer and use the deliveries function.
        // 现在你可以调用上下文邮件发送器 (context mailer) 并使用 deliveries 函数。
        with_settings!({
            filters => cleanup_email()
        }, {
            assert_debug_snapshot!(ctx.mailer.unwrap().deliveries());
        });
    })
    .await;
}