Apitore blog

Apitoreを運営していた元起業家のブログ

SendGrid-Javaでメール送信

はじめに

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で親切な方が教えてくれました。あたたかいネット。その方のブログ記事も紹介してくれました。

今回、私は最新版の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クラスの初期化しかやってくれません(笑)

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/htmltext/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のダッシュボードを解説してください笑