はじめに
ApitoreはGoogle Compute EngineでSpring mail + JavaMail越しにSendGridを使ってメール送信をしています。今回はSpring mail + JavaMailをSendGridの公式SDKであるSendGrid-Javaに置き換えたいと思います。
小話
なぜSpring mail + JavaMailから乗り換えるかというと、以前解説したこちらを見てください。お分かりいただけると思いますが、設定ファイルにユーザー名とパスワードを記載しないといけないのがイケてないですよね?今回の方法でやると、SendGridのWebAPI経由でメール送信ができるようになります。必要なのはAPI Keyのみです。パスワードに比べて漏洩したときの変更コストが少なくて済みます。特に、パスワードを使いまわしている人はAPI Keyの恩恵を感じて頂けるかと(その前に、パスワードを使いまわすな、とツッコミが必要ですが)。
事前準備
SendGridにフリープランでも良いのでアカウントを作ります。私のダッシュボードを見せたくないので画面キャプチャはしませんが、ダッシュボード左側に「Settings」というタブがありまして、その中に「API Keys」という項目がありますのでクリックします。 「Create API Key」を押します。 メール送信をするだけなら、「Restricted Access」で十分です。「API Key Name」を入力し、「Restricted Access」を選択します。 「Mail Send」だけONにします。 API Keyを発行します。一度きりしか表示されないので、コピーしたらどこかに保存しておいてください。
実装
Spring bootな人
Twitterで親切な方が教えてくれました。あたたかいネット。その方のブログ記事も紹介してくれました。
@keigohtr 突然のメンションすみません。もしSpring Bootお使いでしたらAutoConfigureの中にSendGridもあるので簡単にお使いいただけるかと思います。 https://t.co/iCXfwbJA5u
— キクタロー@エバスク役職ボッター (@kikutaro_) 2017年5月25日
今回、私は最新版のSendGrid-Javaを使おうと思います。Mavenはこちら。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>4.0.1</version>
</dependency>
Spring bootにSendGridがあるといいつつも、SendGridクラスの初期化しかやってくれません(笑)
@keigohtr ですです(^^)🍥Beanの定義いらないっていうぐらいの薄いお付き合いですw
— キクタロー@エバスク役職ボッター (@kikutaro_) 2017年5月25日
SendGridのAPI Keyはapplication.properties
にこんな感じで書きます。
spring.sendgrid.apiKey=YOUR-API-KEY
クラスの初期化はこんな感じです。
@Autowired
private SendGrid sendGrid;
ふつうのひと
Spring bootを使わない方はこんな感じです。
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>4.0.1</version>
</dependency>
SendGridのAPI Keyを使ったクラスの初期化はこんな感じです。
SendGrid sendGrid = new SendGrid("YOUR-API-KEY");
メールを送る
実際にメールを送るところを紹介します。ここからはSpring bootな方もそうでない方も一緒です。ちなみに、SendGrid公式のサンプルプログラムはこちらです。1000宛先までなら1回のAPIリクエストで(指定すれば個別のToにして)メールを送れます。sendgrid-java 4.0は3.xとガラッと使い方が変わったみたいです。宛先を扱うクラスとしてPersonalization
を使います。
1件メールを送るサンプルはこちらです。setSandboxMode
の行のコメントアウトを外せば、Sandboxモードになります。実際にメールを送らず、レスポンスどうなるのかなってのを調べられます。下の例ではHTMLメールにしていますが、text/html
をtext/plain
にすればプレーンテキストでメールを送れます。
try {
Mail mail = new Mail();
//送る人
Email fromEmail = new Email();
fromEmail.setName("Sender");
fromEmail.setEmail("sender@example.com");
mail.setFrom(fromEmail);
mail.setReplyTo(fromEmail);
//件名
mail.setSubject("Subject");
//本文
Content cnt = new Content();
cnt.setType("text/html");
cnt.setValue("Contents");
mail.addContent(cnt);
//細かい設定
MailSettings mailSettings = new MailSettings();
Setting sandBoxMode = new Setting();
sandBoxMode.setEnable(true);
//mailSettings.setSandboxMode(sandBoxMode);
mail.setMailSettings(mailSettings);
//送る相手
Personalization personalization = new Personalization();
Email tmp;
// To
tmp = new Email();
tmp.setEmail("to@example.com");
personalization.addTo(tmp);
// Cc
tmp = new Email();
tmp.setEmail("cc@example.com");
personalization.addCc(tmp);
// Bcc
tmp = new Email();
tmp.setEmail("bcc@example.com");
personalization.addBcc(tmp);
mail.addPersonalization(personalization);
//送信
Request request = new Request();
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sendGrid.api(request);
System.out.println(response.getStatusCode());
System.out.println(response.getBody());
System.out.println(response.getHeaders());
} catch (IOException e) {
System.err.println(e);
}
さて、SendGridは1000宛先を1回のリクエストで、しかも個別のToにしてメールを送れます。コストはなるべく抑えたいので、これはやるしかない。ついでに各メールに「○○様」と相手の名前をいれるようにします。ありがたいことに、SendGridはそういう用途に対応したAPIを出しています。
事前準備としてMailEntity
というクラスを新たに作りました。email
が宛先でname
が名前です。
public class MailEntity implements Serializable {
private String email;
private String name;
//Getter, Setterは省略
}
メール本文はこんな感じです。%name%
がポイントです。
%name%様、こんにちは!
私が実際に使っているmethodを紹介します。個別のToで送るためには個別のPersonalization
を作る必要があります。personalization.addSubstitution
のところで、メール本文の%name%
を個別の名前に変換します。今回は個別の名前を設定することしかしませんでしたが、やっていることは本文中の任意の文字列の置換なので、色々と応用が効きます。
public void send(List<MailEntity> tos, String cc, String bcc, String subject, String content) {
try {
Mail mail = new Mail();
//送信する人
Email fromEmail = new Email();
fromEmail.setName("Sender");
fromEmail.setEmail("sender@example.com");
mail.setFrom(fromEmail);
mail.setReplyTo(fromEmail);
//件名
mail.setSubject(subject);
//本文
Content cnt = new Content();
content = content.replace("\r", "");
content = content.replace("\n", "<BR>");
cnt.setType("text/html");
cnt.setValue(content);
mail.addContent(cnt);
//細かい設定
MailSettings mailSettings = new MailSettings();
Setting sandBoxMode = new Setting();
sandBoxMode.setEnable(true);
//mailSettings.setSandboxMode(sandBoxMode);
mail.setMailSettings(mailSettings);
//宛先
for (MailEntity ent: tos) {
Personalization personalization = new Personalization();
Email tmp = new Email();
tmp.setEmail(ent.getEmail());
personalization.addTo(tmp);
personalization.addSubstitution("%name%", ent.getName());
if (cc != null) {
tmp = new Email();
tmp.setEmail(cc);
personalization.addCc(tmp);
}
if (bcc != null) {
tmp = new Email();
tmp.setEmail(bcc);
personalization.addBcc(tmp);
}
mail.addPersonalization(personalization);
}
//送信
Request request = new Request();
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sendGrid.api(request);
System.out.println(response.getStatusCode());
System.out.println(response.getBody());
System.out.println(response.getHeaders());
} catch (IOException e) {
LOG.error("IOException",e);
}
}
おわりに
これでユーザー名とパスワードを設定ファイルに書くというイケてない感じから脱却できたぞ!
個人的な感性でいうと、Personalization
→宛先設定、の構成には冗長さを感じます。他にも細かい所が気になりましたが、これまでの変遷を見ているとSDKは色々と構成変えているようなので、たぶん改善していくんじゃないかなと思います。
最後に、、、誰かSendGridのダッシュボードを解説してください笑