Cloud StorageやApp EngineにデプロイしたSPA(Single Page Application)に問い合わせフォームを作りたいという方は多いはずです。
また、問い合わせページを作成したけど、どのようにメールを送信するべきか迷う方もいらっしゃると思います。
そういった方を対象に、Google Cloudの公式ドキュメントで推奨している方法を利用して、問い合わせフォームからメールを送信する方法を紹介します!
完成物のイメージです。
- Cloud Functionsにメール送信用のAPIを公開(python)
- SendGridのWebAPIを利用したメール送信
- 1ヶ月12,000通のメールまで無料
目次 非表示
概要
この記事で紹介する方法では、Cloud FunctionsにSendGridを利用したメール送信機能を持つアプリケーションをデプロイし、デプロイしたアプリケーションに対してSPAからリクエストを送信することで問い合わせ機能を実現します。
まず、SendGridやCloudFunctionsnを利用する理由や利点について説明します。
実装方法だけ知りたいという方はこの章は飛ばしてください。
SendGridとは
SendGridは、クラウドベースのSMTPプロバイダで、メール送信が可能なwebサービスです(公式サイト:https://sendgrid.kke.co.jp/)。
2021年6月では、Google CloudにはAWSのAmazon SESのようなサービスはありません(ないよね…?)。
そのため、メール送信方法として、公式ドキュメントにおいてもSendGrid, Mailgun, Mailjetといったサードパーティのメールサービスを利用した方法を紹介しています(例:SendGrid でのメールの送信)。
これらの中でも、下記の利点があるので、個人的にはSendGridの利用がおすすめです。
- 毎月12,000通まで送信無料
- リファレンスがしっかりと整備されている(公式リファレンス:https://sendgrid.kke.co.jp/docs/API_Reference/index.html)
- githubに公式が複数の言語に対してサンプルプログラムを公開している(github:https://github.com/sendgrid/)
- WebAPIにリクエストするだけでメール送信可能
SendGridのgithubには、python, PHP, java, nodejsなどの言語ごとにリポジトリが作成されており、READMEに簡単なサンプルプログラムが公開されています。
READMEを読むだけで公式の推奨利用方法がわかるので、非常に助かりますね。
また、毎月12,000通まで無料で利用できるというのも非常に嬉しいです。
Cloud Functionsの利用理由
GCPの公式ドキュメントではCompute Engine(CE)を利用した方法を紹介していますが、CEではゼロスケーリングができないのでお金がかかってしまいます。
従量課金性+ゼロスケーリング可能+メール送信機能のみという特徴を考慮して、Cloud Functionsにメール送信アプリケーションをデプロイすることにしました。
ただ、CEやCloudFunctionsの他にAppEngineでも利用可能なはずなので、好きなプラットフォームで良いと思います。
メール送信機能の実装
それでは作成していきます、全体の流れは下記になります。
- SendGridのアカウント作成
- SendGridのAPIキーの取得
- プログラムの作成
- Cloud Functionsへのデプロイ
SendGridのアカウント作成
公式サイトからアカウントの作成を行います(https://sendgrid.kke.co.jp/)。
気をつけるべきポイントとして、アカウントの作成に約1営業日かかります。
アカウントは早めに作成しましょう。
SendGridのAPIキーの取得
SendGridでアカウント登録後、アカウント名が送られてくるので、そのアカウントでログインします。
ログイン後、”Settings”タブから”API Keys”をクリックします。API Key Nameを入力して、Full Access権限でキーを作成します。
プログラムの作成
Cloud Functions上で動作するプログラムを作成します。
まず、プログラムはこちらです。リクエストのバリデーションなどは自分で追加してください。
from flask import Flask, request, make_response
import os
import sendgrid
from sendgrid.helpers.mail import Email, To, Content, Mail
def sample(request): # 取得したAPIKeyを環境変数SENDGRID_API_KEYにいれる SENDGRID_API_KEY = os.environ.get('SENDGRID_API_KEY') TO_EMAIL = os.environ.get('TO_EMAIL') FROM_EMAIL = os.environ.get('FROM_EMAIL') req_args = request.args sg = sendgrid.SendGridAPIClient(api_key=SENDGRID_API_KEY) message = ( 'from: {}\n' 'Email: {}\n' 'msg:\n{}'.format( req_args["name"], req_args["email"], req_args["msg"]) ) mail = Mail( Email(FROM_EMAIL), To(TO_EMAIL), "Email from Cloud Functions", Content("text/plain", message) ) _ = sg.client.mail.send.post(request_body=mail.get()) return make_response('', 200)
if __name__ == '__main__': app = Flask(__name__) @app.route('/sample', methods=['GET']) def call_function(): return sample(request) app.run(debug=True, host='0.0.0.0', port=5000)
Cloud Functionsへのデプロイで必要なrequirements.txtは下記です。
blinker==1.4
click==8.0.1
Flask==1.0.2
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
mccabe==0.6.1
pycodestyle==2.7.0
pyflakes==2.3.1
python-http-client==3.3.2
sendgrid==6.7.0
starkbank-ecdsa==1.1.1
toml==0.10.2
Werkzeug==2.0.1
あとは、ローカル環境で動作確認後、Cloud Functionsにデプロイしましょう。
下記では.env.yaml
を利用してAPIキーを環境変数に代入していますが、SecretManagerを利用するほうがセキュリティ的には正しいと思います。
# ローカル環境でのテストリクエスト例
$ curl -v "http://localhost:5000/sample?email=test@gmail.com&msg=hoge&name=huga"
# 問題がなければ、Cloud Functionsへデプロイ
# デプロイコマンドの例
$ gcloud functions deploy sample_for_sendgrid \ --runtime python38 \ --trigger-http \ --allow-unauthenticated \ --region=us-central1 \ --env-vars-file .env.yaml
# Cloud Functionsに対して、動作確認
$ curl -v "https://.../sample?..."
SPAとの連携
連携というほどのものではありませんが、、、
SPAの問い合わせフォームのボタンを押したときに、上記で作成したCloud FunctionsのエンドポイントへGETリクエストを送信するように変更しましょう。
上記では、クエリパラメータとしてemail, msg, nameを適当に使いましたが、このあたりは各自適当に変更してください。
まとめ
この記事では、Cloud Functions + SendGridを利用してメール送信機能を持つAPIを作成しました。
SPAから作成したエンドポイントに対してリクエストを送ることで、問い合わせフォームからメールを送信することが可能になります。
Google検索でGCP上のメール送信方法を調べると、Firebase+gmailを利用した方法が多く見られます。ただ、Google Cloudで完結可能なので、わざわざFirebaseを利用する必要はないです(実際にはFirebaseもGoogle Cloud上で動作していますが…)。
また、Gmailのアカウント情報+信頼性の低いアプリケーションからのログインを許可する必要があるので、セキュリティ的にもよろしくないかなと個人的には考えています。