RustからTwilio SendGridのメールを送信する方法
- 2022年9月15日
- by 菊田 洋一
- Category: 技術ネタ
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にはhyperやreqwest、surfなど様々な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のようなパッケージマネージャも標準で備えられていて、モダンで書きやすい印象でした。今後さらに勉強していきたいと思います。