RustからTwilio SendGridのメールを送信する方法

RustからTwilio SendGridのメールを送信する方法

Twilio SendGridサポートチームの菊田(@kikutaro_)です。最近、プログラミング言語の「Rust」を触り始めました。勉強の一環としてSendGridを使ったメール送信を試してみたので、ブログにまとめたいと思います。

Windows 10のDocker上に構築した以下の環境を利用しました。

  • Debian 11.4
  • Rust 1.63.0

lettreを使った送信

テキストメール

SMTPによる送信はRust向けのメール送信ライブラリlettreを使いました。まずはRust標準のパッケージマネージャ「Cargo」で新規にプロジェクトを作成してlettreを追加します。

cargo new smtp

Cargo.tomlに以下の内容を追記します。

[dependencies]
lettre = "0.10.0"

ビルドするとなぜかエラーになってしまいました。

cargo build
   Compiling openssl-sys v0.9.75
error: failed to run custom build command for `openssl-sys v0.9.75`

証明書関連のライブラリが足りないのが原因のようです。私の環境では最終的にpkg-configとlibssl-devをインストールすることで解決しました。

apt-get install pkg-config libssl-dev

lettreのExampleを元に、認証や接続の情報をSendGridのものに変更します。

use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};

fn main() {
    let email = Message::builder()
    .from("FromName <from@example.com>".parse().unwrap())
    .to("ToName <to@example.com>".parse().unwrap())
    .subject("Rustでメール送信!")
    .body(String::from("RustからSendGridのSMTPで送ってるよ!"))
    .unwrap();

    let creds = Credentials::new("apikey".to_string(), "SG.xxxxxxxx".to_string());

    let mailer = SmtpTransport::relay("smtp.sendgrid.net")
        .unwrap()
        .credentials(creds)
        .build();

    match mailer.send(&email) {
        Ok(_) => println!("送信成功"),
        Err(e) => panic!("送信失敗: {:?}", e),
    }
}

(参考)

ビルドエラーが発生しないことを確認して実行します。

rust build
rust run

届いたメール - テキストメール

メールが無事に届きました!思っていたよりかなり簡単です。

マルチパートメール

今度はHTMLパートとテキストパートの両方を含むマルチパートメールを送信します。lettreではマルチパートを直感的に実装できるのでありがたいです(コード27行目)。

use lettre::transport::smtp::authentication::Credentials;
use lettre::{
    message::{header, MultiPart, SinglePart},
    SmtpTransport, Message, Transport,
};

fn main() {
    let html = r#"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" /><!--[if !mso]><!-->
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" /><!--<![endif]-->
      </head>
      <body>
        <div style="display: flex; flex-direction: column; align-items: center;">
            <h2>RustからSendGridのSMTPで送ってるよ!</h2>
            <h4>HTMLパート</h4>
        </div>
      </body>
    </html>"#;

    let email = Message::builder()
    .from("FromName <from@example.com>".parse().unwrap())
    .to("ToName <to@example.com>".parse().unwrap())
    .subject("Rustでマルチパートメール送信!")
    .multipart(
        MultiPart::alternative()
            .singlepart(
                SinglePart::builder()
                    .header(header::ContentType::TEXT_PLAIN)
                    .body(String::from("テキストパート")),
            )
            .singlepart(
                SinglePart::builder()
                    .header(header::ContentType::TEXT_HTML)
                    .body(String::from(html)),
            )
    )
    .unwrap();

    let creds = Credentials::new("apikey".to_string(), "SG.xxxxxxxx".to_string());

    let mailer = SmtpTransport::relay("smtp.sendgrid.net")
        .unwrap()
        .credentials(creds)
        .build();

    match mailer.send(&email) {
        Ok(_) => println!("送信成功"),
        Err(e) => panic!("送信失敗: {:?}", e),
    }
}

受信したメールを確認するとHTMLパートの内容が無事に表示されました。

届いたメール - マルチパートメール

テキストパートは受信したメールのソースで確認してみましょう。マルチパートメールのテキストとHTMLの区切りはboundaryで定義されています。受信したメールでは「boundary="inhjyJTg8beDfQ6gmb4kJyd5N0WQwCN8qF15HEHg"」となっていたので、この文字列を検索します。

受信したメールのソース

「Content-Type: text/plain」の部分がテキストパートです。base64でエンコードされているのでデコードします。

echo '44OG44Kt44K544OI44OR44O844OI' | base64 -d
テキストパート

送信した内容と一致していますね。マルチパートメールが送れたことを確認できました。

sendgrid-rsを使った送信

続いてWeb APIによる送信を試しました。Rustにはhyperreqwestsurfなど様々なHTTPクライアントがあるのでどれが良いのか悩んでいたところ、sendgrid-rsというラッパーライブラリをみつけました。非公式のライブラリではありますが、公式サイトでも紹介されています。

use sendgrid::v3::*;

fn main() {
    let p = Personalization::new(Email::new("to@example.com"));
    let message = Message::new(Email::new("from@example.com"))
        .set_subject("Rustでメール送信!")
        .add_content(
            Content::new()
                .set_content_type("text/plain")
                .set_value("sendgrid-rsを使ってWeb APIから送ってるよ!"),
        )
        .add_personalization(p);

    let api_key = "SG.xxxxxxxxx".to_string();
    let sender = Sender::new(api_key);
    let code = sender.send(&message);
    println!("{:?}", code);
}

他言語の公式ライブラリ並みに簡単な実装で送信できました。あくまでも非公式のライブラリなので、実際の運用で利用する場合には事前の動作確認やサポート状況をよく確認してください。

コード

今回書いたコードはすべてこちらにまとめました。

おわりに

最近Rustに関する情報をよく見かけるので勉強を兼ねて触ってみました。C言語やC++に置き換わる言語とも聞いていたのでとっつきにくいのかな…?と思っていましたが、Cargoのようなパッケージマネージャも標準で備えられていて、モダンで書きやすい印象でした。今後さらに勉強していきたいと思います。

アーカイブ

メールを成功の原動力に

開発者にもマーケターにも信頼されているメールサービスを利用して、
時間の節約、スケーラビリティ、メール配信に関する専門知識を手に入れましょう。