✉️

SpringでMarkdown形式の文字列をHTML形式にしてメール送信する

2022/09/23に公開

SpringBootを使ってメール送信を行ってみます。

やること

  • MailHogを使ってメール送信テストをする
  • Markdown形式の文字列をHTML形式にして送信する

開発段階で実際のメールを使ったら大変な事になっちゃうのでMailHogを利用します。

MailHogの準備

dockerを使ってMailHogを利用開始します

docker-compose.yaml
version: '3'
services:
  mailhog:
    image: mailhog/mailhog
    ports:
      - 1025:1025
      - 8025:8025
  • 1025
    • smtpserver
  • 8025
    • WebUI

以下のコマンドでコンテナを立ち上げます

docker-compose up -d

完了後はWebUIのポートからメール画面を見ることができます

ttp://localhost:8025/

これにてMailHogの準備は完了です。

SpringBootアプリケーションの作成

以下のサイトでgradleのアプリケーションを作成します

https://start.spring.io/

メールの設定

とりあえずメールの送信を試したいのでspring-boot-starter-mailの依存を追加しておきます

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-mail'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

メールの設定をapplication.yamlに行います

application.yaml
spring:
  mail:
    host: 192.168.11.13
    port: 1025

hostの部分はマシンのIPアドレスを入力します
portの部分はsmtpserverのポート

メールの作成

メールを利用するには以下のようなクラスが必要です

  • JavaMailSender
  • MimeMessageHelper
  • MimeMessage

MimeMessageはMimeMessageHelperを介して宛先や本文などのメール情報を作成します。
MimeMessageHelperでは設定する値のチェックや、HTMLメールにするかどうかなどの処理を行います。
JavaMailSenderがMimeMessageを送信します

参考

https://www.baeldung.com/spring-email

MimeMessageHelperで行う処理はConsumer系に任せるような実装にします
例外を扱うので関数型インターフェースを新たに定義しておきます

@FunctionalInterface
public interface UseInstance <T,X extends Throwable>{
    void accept(T instance) throws X;
}

上記の関数型インターフェースを利用してメール送信を行うクラスを作成します

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

@Component
public class SendMail {

    private static JavaMailSender sender;

    @Autowired
    public void setJavaMailSender(JavaMailSender sender){
        SendMail.sender = sender;
    }

    public static void sendMail(UseInstance<MimeMessageHelper, MessagingException> block){
        MimeMessage message = SendMail.sender.createMimeMessage();
        try {
            MimeMessageHelper messageHelper = new MimeMessageHelper(message,true);
            block.accept(messageHelper);
            SendMail.sender.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}

簡単にメール送信ができるかどうかだけテストしてみます

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class SendMailTest {

    @Autowired
    private SendMail sendMail;

    @Test
    public void test_sendMail(){
        SendMail.sendMail( helper -> {
            helper.setFrom("toma@kamijo.com");
            helper.setTo("mikoto@misaka.com");
            helper.setSubject("メールタイトルのテスト");
            helper.setText("本文のテスト");
        });
        assertTrue(true);
    }
}

実行して先ほどのmailhogの画面を更新するとメール履歴があることがわかります

先ほど設定した内容でメールが送信されています。

これにてメール送信の確認は完了です。

Markdown形式

Markdown形式の文字列を扱うために以下の依存モジュールを追加しておきます

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-mail'
	// 追加
	implementation 'com.vladsch.flexmark:flexmark-all:0.64.0'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

https://github.com/vsch/flexmark-java

Markdown形式の文字列からHtml形式の文字列に変換できるクラスを作っておきます

import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;

public class Html {
    public static String toHtml(String markdown) {
        MutableDataSet options = new MutableDataSet();
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        Node document = parser.parse(markdown);
        String html = renderer.render(document);
        return html;
    }
}

テストにて利用してメール送信してみます

@SpringBootTest
class SendMailTest {

    @Autowired
    private SendMail sendMail;

    @Test
    public void test_sendMail(){
        String message = "# Title\n"
                + "* one\n"
                + "* two\n"
                + "* three\n";
        SendMail.sendMail( helper -> {
            helper.setFrom("toma@kamijo.com");
            helper.setTo("mikoto@misaka.com");
            helper.setSubject("メールタイトルのテスト");
            helper.setText(Html.toHtml(message),true);
        });
        assertTrue(true);
    }
}
  • setTextメソッドの第二引数をtrueにするとHTMLメールとして送信できます

実行してメールを確認するとHTML形式で表示されています

とても簡単に扱えることが確認できました

これにてMarkdown形式の文字列をHtmlに変換して表示する確認は完了です

メール送信画面を作ってメールを送信する

既に今回の目標は達成していますが、折角なのでメール送信画面を作って、メール送信を行ってみます。

まずは以下の依存を追加しておきます。

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-mail'
	implementation 'com.vladsch.flexmark:flexmark-all:0.64.0'
	// 追加
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	// 追加
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

次にメールフォーム用のクラスを作成します

public class MailForm {

    private String subject;

    private String message;

    public String getSubject(){
        return this.subject;
    }

    public void setSubject(String subject){
        this.subject = subject;
    }

    public String getMessage(){
        return this.message;
    }

    public void setMessage(String message){
        this.message = message;
    }
}
  • 本当はLombokを使うと楽です
  • このクラスとHTMLのformタグ内の要素が紐づきます

コントローラーを作成します

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class MailController {

    @GetMapping("/mail")
    public String mail(MailForm mailForm){
        return "mail";
    }

    @PostMapping("/mail/send")
    public String send(MailForm mailForm){
        SendMail.sendMail(helper -> {
            helper.setFrom("kakine@teitoku.com");
            helper.setTo("measure@heart.com");
            helper.setSubject(mailForm.getSubject());
            helper.setText(Html.toHtml(mailForm.getMessage()),true);
        });
        return "redirect:/mail";
    }
}
  • GetMappingの方
    • htmlファイルのmail.htmlは後で作成します
    • htmlファイルからフォームを扱うため、紐づくフォームクラスを引数に取っています
  • PostMappingの方
    • 本来はここでバリデーション処理など行います
    • mailFormには画面から渡された値がバインドされています

次に画面を作成します

mail.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>メール送信</title>
</head>
<body>
<h1>メール送信</h1>
<form th:action="@{/mail/send}" th:object="${mailForm}" method="post">
    <div>
        <input th:type="text" th:field="*{subject}" />
    </div>
    <div>
        <textarea th:field="*{message}" rows="20" cols="50"></textarea>
    </div>
    <div>
        <button type="submit">送信する</button>
    </div>
</form>
</body>
</html>

以下にアクセスするとフォームが表示されます

ttp://localhost:8080/mail

試しに送信してメールを確認してみます

送信

簡単にメール送信とMarkdown形式の文字列をHTML形式に変換することができました。

Discussion