域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

通过 lettre 发送邮件

本章将讨论使用 lettre 在 rust 中实现发送邮件。我们将分别使用 Gmail 和 Mail.ee 来作测试。

Gmail 的设置

  • 开启了两步验证
  • 创建了应用专用密码

详细操作,请查看官方文档

Mail.ee 的设置

和 Gmail 相比,Mail.ee 的操作就简单多了。注册并登录 Mail.ee,然后:

  1. 点击右上角展开菜单按钮
  2. 点击 EMAIL OPTIONS
  3. 点击 Outlook, email programs
  4. 点击 Enable
  5. 最终显示了专用密码以及 IMAP/SMTP 的连接地址和端口

依赖

[dependencies]
# ...
lettre = {version="0.10",features=["tokio1-native-tls"]}

配置

示例一:Gmail配置

EMAIL.USERNAME='[email protected]'
EMAIL.PASSWORD='<你的专用密码>'
EMAIL.HOST='smtp.gmail.com'

示例二:Mail.ee配置

EMAIL.USERNAME='[email protected]'
EMAIL.PASSWORD='<你的专用密码>'
EMAIL.HOST='mail.mail.ee'

模型

我们定义了一个模型,用于映射每一封邮件:

pub struct Email {
    pub from: String, // 发件人地址。通常和配置中的username一样
    pub to: String, // 收件人地址
    pub subject: String, // 邮件主题
    pub body: String, // 邮件内容
}

impl Email {
    /// 转换为 letter 的 Message 类型
    pub fn to_message(&self) -> Result<Message> {
        Message::builder()
            .from(self.from.as_str().parse().unwrap())
            .to(self.to.as_str().parse().unwrap())
            .subject(self.subject.as_str())
            .header(ContentType::TEXT_PLAIN) // 如要发送 HTML 邮件,将这个换成 ContentType::TEXT_HTML
            .body(self.body.clone())
            .map_err(Error::from)
    }
}
/// 同步发送
pub fn sync_send(cfg: &EmailConfig, m: &model::email::Email) -> Result<Response> {
    let message = m.to_message()?;

    let creds = Credentials::new(cfg.username.clone(), cfg.password.clone());

    let mailer = SmtpTransport::relay(&cfg.host)
        .map_err(Error::from)?
        .credentials(creds)
        .build();

    mailer.send(&message).map_err(Error::from)
}
  • 生成一个邮件内容:let message = m.to_message()?;
  • 生成身份验证信息: let creds = Credentials::new(用户名, 密码);
  • 连接服务器: SmtpTransport::relay()
  • 发送邮件: mailer.send(&message)

测试

#[test]
fn test_sync_send_email() {
    let cfg = get_cfg().unwrap();
    let m = model::email::Email {
        from: format!("{}", &cfg.username),
        to: format!("[email protected]"),
        subject: format!("试试同步发送"),
        body: format!("你好呀,这是用lettre同步发送的邮件!"),
    };
    let resp = super::sync_send(&cfg, &m).unwrap();
    tracing::info!("{:?}", resp);
}

/// 异步发送
pub async fn send(cfg: &EmailConfig, m: &model::email::Email) -> Result<Response> {
    let message = m.to_message()?;

    let creds = Credentials::new(cfg.username.clone(), cfg.password.clone());

    let mailer = AsyncSmtpTransport::<Tokio1Executor>::relay(&cfg.host)
        .map_err(Error::from)?
        .credentials(creds)
        .build();

    mailer.send(message).await.map_err(Error::from)
}

和同步发送大同小异:

测试

#[tokio::test]
async fn test_async_send_email() {
    let cfg = get_cfg().unwrap();
    let m = model::email::Email {
        from: format!("{}", &cfg.username),
        to: format!("[email protected]"),
        subject: format!("试试异步发送"),
        body: format!("你好呀,这是用lettre异步发送的邮件!"),
    };
    let resp = super::send(&cfg, &m).await.unwrap();
    tracing::info!("{:?}", resp);
}

本章代码位于02/lettre分支。

要查看完整内容,请先登录