✉️

Ruby on RailsでSendgrid(Web API)メール送信を実装

2021/04/20に公開1

業務でSendgridのSMTPを用いてメールをユーザー宛に送信していたが、送信されるメール毎にカテゴリーを付与し、コンバージョン率を調査したいとの社内マーケからの要望を受けた。

そのため、SMTPでカテゴリーを付与しようとしたが、カテゴリー設定がSMTPではできないことが判明し、Web APIにて実装しようと思ったところ、意外と参考文献が少なかったので備忘録も兼ねて記事とした🚬
ちなむと今回は、Web APIを用いてメール送信するところが難所なのではなく、カテゴリー付与するところで詰まりました。

また、今回APIの呼び出しに使用したのはsendgrid-rubyというgemである。

https://github.com/sendgrid/sendgrid-ruby

SendgridのAPI取得

APIの取得に関しては公式ドキュメントを参照して取得して欲しい。
メール送信など単純な使用目的で取得する場合はfull accessを選択してAPI生成すれば問題なし✌️

https://sendgrid.kke.co.jp/docs/Tutorials/A_Transaction_Mail/manage_api_key.html

コード実装

ここからの実装の流れは以下2つの記事を参照した。

https://techracho.bpsinc.jp/yamada/2018_12_22/67222

https://qiita.com/sheepland/items/0bbf5c6f9fba08caf0a6

最終的なコード

RailsはデフォでAction Mailer(smtp)を使用するので、新規でdelivery methodを追加してあげる。

今回実装したファイルは以下。

# config/initializers/sendgrid.rb

ActionMailer::Base.add_delivery_method :sendgrid, Mail::SendGrid,
  api_key: Settings.sendgrid_api #settings.local.ymlの中にあるAPIキーを呼び出す。
# app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  def set_sendgrid_settings(category)
    ActionMailer::Base.delivery_method = :sendgrid
    headers['category'] = category
  end
end
# lib/mail/send_grid.rb

class Mail::SendGrid
  def initialize(settings)
    @settings = settings
  end

  def deliver!(mail)
    sg_mail = set_mail_information(mail)
    add_content_and_category(sg_mail, mail)
    sg = SendGrid::API.new(api_key: @settings[:api_key])
    sg.client.mail._('send').post(request_body: sg_mail.to_json)
  end

  private

    def set_mail_information(mail)
      from = SendGrid::Email.new(email: mail.from.first)
      to = SendGrid::Email.new(email: mail.to.first)
      subject = mail.subject
      content = SendGrid::Content.new(type: 'text/plain', value: mail.body.parts[0].body.raw_source)
      SendGrid::Mail.new(from, subject, to, content)
    end

    def add_content_and_category(sg_mail, mail)
      sg_mail.add_content(SendGrid::Content.new(type: 'text/html', value: mail.body.parts[1].body.raw_source))
      sg_mail.add_category(SendGrid::Category.new(name: mail[:category].value))
    end
end

メールが送られるまでの流れ

  • app/mailers/application_mailer.rbで定義したメソッドset_sendgrid_settings('カテゴリー名')をmailerファイルの中で呼び出す。

例 )

以下test_mailer.rbのアクション内で定義したset_sendgrid_settings('カテゴリー名')を呼び出すことで、「test_mailerメソッドのdelivery_methodはsendgridのWeb APIを通して送信しますよ。」と指示する。
※引数でカテゴリー名を渡すことで、sendgridへカテゴリー名も付与することができる。

# app/mailers/test_mailer.rb
class TestMailer < ApplicationMailer
  def test_mail
    set_sendgrid_settings('This is test category')
    mail(from: 'from@example.com', to: 'to@example.com', subject: 'テストメール')     
  end
end
# app/views/test_mailer/test_mail.text.erb
SendGrid送信テスト

  • set_sendgrid_settings('カテゴリー名')によって、lib/mail/send_grid.rbで定義したdeliver!メソッドが使用できる。

以下のinitializeによって、config/initializers/sendgrid.rbが呼び出されAPIを使用することができ、@settingsにAPIキーが代入される。

# lib/mail/send_grid.rb
class Mail::SendGrid

  def initialize(settings)
    @settings = settings
  end
~

  • mailerから渡ってきたmail変数をsendgrid-rubyの所定のフォーマットに則って代入する。

以下のset_mail_information(mail)プライベートメソッドにてgemのフォーマットに則って代入する。
SendGrid::Email.new()はgem特有のもの。詳しくは公式まで。

# lib/mail/send_grid.rb
class Mail::SendGrid

~

  private

    def set_mail_information(mail)
      from = SendGrid::Email.new(email: mail.from.first)
      to = SendGrid::Email.new(email: mail.to.first)
      subject = mail.subject
      content = SendGrid::Content.new(type: 'text/plain', value: mail.body.parts[0].body.raw_source)
      SendGrid::Mail.new(from, subject, to, content)
    end

~

以上の流れでメールは送信できる。


詰まった問題点

そもそも業務でsendgridを使用しているので、text/plain形式だけではなく、html形式のメールも送信するマルチパートメールを採用している。(というかどこの企業もそうだと思うが…)

上記までだと、text/plain形式のメールだけしか送れず、なんならhtml.erbファイルもある場合はエラーが出て送信できない。

あと、カテゴリーに関してもaction mailerのMail::Messageに含まれる引数しか渡せないため、独自に変数を渡すことは不可能だったので、別の方法で実装するしかなかった。

  • マルチパートメールどうやって送るの?
  • カテゴリー名どうやって付与するねん…

解決方法

結論、gemの特性なのか知らないが、先にtext/plain形式のメールを指定しなければならないらしい。なので、html形式はadd_categoryというgemのメソッドを使用して追加することに成功した。

カテゴリーは、mailerのactionごとに指定したい場合には、header[:category]として含める必要があることがわかった。
※カテゴリー名はUS-ASCII文字セットのみ対応(日本語不可)ということにも注意。

該当箇所

html形式のメールはmail.body.partsの2番目に含まれているので、mail.body.parts[1]で指定してあげる。(1番目に格納されているのはtext/plain形式。)

# lib/mail/send_grid.rb

class Mail::SendGrid

~

    def add_content_and_category(sg_mail, mail)
      sg_mail.add_content(SendGrid::Content.new(type: 'text/html', value: mail.body.parts[1].body.raw_source))
      sg_mail.add_category(SendGrid::Category.new(name: mail[:category].value))
    end
end

以上でmailerのアクション毎にカテゴリー名を付与した上で、Web APIを経由しメール送信することが可能になった。

Sengridのダッシュボード上でもカテゴリー分け毎のコンバージョン率も確認できて、Webマーケからも感謝されました。めでたしめでたし。

Discussion

h th t

コメント失礼致します。
lib/mail/send_grid.rbのファイルにbinding.pryを仕込んで
デバッグすることは可能でしょうか?lib/mail/send_grid.rbがinitializeのタイミングで
呼び出されているかがわかりません。