Railsで作成中のWebアプリケーションにSendGridを使用しています。SendGridを通じてメールを「送信」する方が多いと思いますが、「Inbound Email Parse Webhook」を利用してアプリケーションでメールを受信することにも使用できます。
SendGridでのメール「受信」について記事が少ないような気がしましたので記録に残しておきます。
##環境は以下の通りです。
・Ruby2.5.7 ※他の言語でも可能
・rails5.2 ※他のフレームワークでも問題無し
・Cloud SQL (PostgreSQL) ※他のDBでも問題なし
・Webサービス自体のサーバー:Google App Engine Standard
・独自ドメイン利用
##目次
大まかなデータ処理の流れ
利用例
実装手順
ハマったポイント
実装に参考にさせていただいたページ
##大まかなデータ処理の流れ
①誰かから独自ドメインのメールに送られてきたメールをSendGridで一度受けてもらい
②SendGridのAPIに自分のWEBサービスにJSON形式でPostしてもらう
③Postされたデータ内容を自らのWebアプリケーション(自分の場合はRails)でPostgreSQLに保存(たまたまPostgreSQLを利用しているだけでなんでも構いません)
④必要に応じて保存されたデータをViewから内容を確認できる
という仕組みです。
メール内容は「to」「 from」「text」などのパラメーターで送られてきますので、そのパラメーターをコントローラーで処理します。
##利用例
・送信者のメールアドレスにより処理を分岐させることや
・日記をメールでつける
・メーリングリストのように送られたメールを転送
などセキュリティーは要注意ですが実現可能です。
##実装手順
前提:SendGridはアカウント登録済み、ドメイン認証済み
※受信はFreePlanでも何通でも無料(2020年4月現在)
以下のABCの設定が必要です。
###A:SendGrid側の設定(2点)
ホーム画面→セッティング→Inbound Parse→Add Host & URLから
####Ⅰ Postして欲しいURLを指定
例:自分のWebサービスのURL+「email_parse」 など任意のURLを設定
####Ⅱ どのドメインへのメールを処理するか
※サブドメイン指定可能ですがユニークである必要があります。
※@より前の部分にかかわらず指定したドメインあてのメールが全てPostされますので私はサブドメインで設定しました。
###B:DNSプロバイダーの設定(1点)
SendGrid でメールを受け取ることを許可するため、お名前.comなどでDNS登録が必要です。MXレコードを「mx.sendgrid.net.」「priority 10」 で設定
###C:Webアプリケーション側の設定
####Ⅰ SendGrid上で設定したURLをroutesに設定
####Ⅱ メールが届き次第sendgridからPostされます。
parameter はparams[:to] params[:from] params[:text]などでメールの内容が取得できますのでDBに保存するなり好きな処理が可能になります。
しかしメールアドレスにより処理したい場合、params[:from]では「氏名+<"aaa@bbb.com">」のような形で送られてくることもありますので、メールアドレスのみを取得したい場合は「params[:envelope]」から取得できました。
##ハマったポイント
①GriddlerというGemが用意されていますが、チュートリアル通り実装しても500エラーが出てくるので利用を諦めました。
※500エラーとしか内容が確認できず手っ取り早く自分で処理しようと思いました、、
②rails 5.2 がパラメーター化されたJsonデータの処理に詰まりました
params[:something] で取得するデータの型はJsonでもHashでもなくActionController::Parametersです。開発環境のターミナル上でParameter {ここに内容が入ってます}と表示されているデータがそれにあたります。sendgridからjson形式で送られるデータをrailsが自動で変換するはずです。その際にメタ文字をエスケープする「\」や改行を意味する「\n」などが入ります。例えば以下のような形式で送られてきます。
Parameters:
{"envelope"=>"{\"to\":[\"aaa@bbb.com\"],\"from\":\"ccc@ddd.com\"}"}
このような場合で「aaa@bbb.com」のみを取得したい場合でも、直接params[:email][:to]のような形ではアクセスできませんでしたので①〜③の順を追って処理しました。
①
atesaki = params[:email] で
{\"to\":[\"aaa@bbb.com\"],\"from\":\"ccc@ddd.com\"} のみを切り出し
②
①の形になればJsonとして認識されるのでJson→Hashへ変換します。=変換することにより不要な「\」や「\n」の部分がなくなります。
hash = JSON.parse(atesaki, symbolize_names: true)
※JSON.parse(json 形式のデータ)の オプションで, symbolize_names: trueを入れるとhash[:to]で受け取ることができる。
※ symbolize_names: true無しの場合hash["something"]で値を受け取ることができる。
③
まだ取得できたデータの["aaa@bbb.com"]は配列なので
address = hash[:to][0]
として配列のn番目のデータを指定してようやく"aaa@bbb.com"のみを抽出できました。
※これは自分の基礎知識不足で本来ハマるポイントでは無いのかもしれません、、、が同じポイントで悩む方のために共有しておきます。
###実装に参考にさせていただいたページ
https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/