AWS Lambda(python) / API Gateway / DynamoDB / SES でWebサイトのお問い合わせAPIを作る(DB保存,メール,slack連携)
サーバーレスでwebサイトを構築をすることがあったので、Lambdaネタです。
ググればすぐ出てくるような話が主ですが、ユースケースが合う人の参考になれば。
作成部分は簡単にしか書いていませんので詳しくはドキュメントを参照願います。
また、pythonで書いているので他言語は読み替えていただければと思います。
LambdaとAPI Gatewayを作成する
Lambda関数を作成する
AWS WebコンソールよりLambda関数を作成します。
ロールに権限が必要になるのでDynamoDBとSESにアクセスできるようにしておきます。API Gatewayを作成する
コンソールよりAPI Gatewayに行き新しいAPIの作成を押下し、API名とエンドポイントタイプ(リージョン)を入力/選択して作成します。
空のAPIにリソースを設定します。アクションからメソッドの作成を選択し、POSTを作ります。Lambda関数の欄に先ほど作成したLambda関数を入力して保存。
また、CORSは有効化しておきます。POSTメソッドを選択し、アクションからCORSの有効化を行います。
あとはAPIをデプロイすればAPI Gateway側の基本設定は終了で、APIを実行できるようになります。
- 最終的にLambda側のDesinerはこんな感じ(CloudWatchとS3にも権限を与えています)
前提:お問い合わせのPOST内容
下記のようなjsonが送られてくるとします。関数のテストイベントの中身をこれにしておきます。
{ "item": { "company_name": "テスト株式会社", "name": "テスト名前", "tel": "000-0000-0000", "email": "hogehoge@test", "content": "内容" } }
1. Lambdaで入力されたものをDynamoDBに保存する
まず、DaynamoDBに contacts
というテーブルを作成しておきます。
lambdaのコードは元々はこんな感じ
import json def lambda_handler(event, context): # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
event
で入力を受け取ります。APIの場合POSTされてきた中身がここに入ってきます。
POSTされてきたデータをlambdaに登録する処理を入れたコードはこんな感じ。
import json from datetime import datetime import boto3 from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') def lambda_handler(event, context): item = event['item'] # 対象テーブル table = dynamodb.Table('contacts') # 実行時間を登録しておく item['received_at'] = datetime.now().strftime("%Y/%m/%d %H:%M:%S") # テーブル登録 table.put_item( Item=item ) # 成功 return { 'statusCode': 200, 'body': json.dumps('success!') }
登録された時間をテーブル内のデータとして持っておきたかったので datetime.now
を使用しています。注意点としては日本時間にする場合はLambda関数の環境変数にタイムゾーンを設定しておく必要がある点です。
2. Lambdaでお問い合わせ内容をメールで管理者に知らせる
先にSESでドメインから送信できるように設定しておく必要があります。
仮に test.com
というドメインを持っていて送信できる状態とします。
メール送信用に以下のような関数を用意します。
import boto3 MAIL_REGION = "us-east-1" def send_email(source, to, subject, body): client = boto3.client('ses', region_name=MAIL_REGION) response = client.send_email( Source=source, Destination={ 'ToAddresses': [ to, ] }, Message={ 'Subject': { 'Data': subject, }, 'Body': { 'Text': { 'Data': body, } } } ) return response
呼び出しは send_email("no-reply@test.com", "admin@test.com", "タイトル", ”本文”)
といった感じです。
これを踏まえて先ほどのDB登録を行うコードに付け加えてみます。
import json from datetime import datetime import boto3 from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') MAIL_REGION = "us-east-1" def send_email(source, to, subject, body): client = boto3.client('ses', region_name=MAIL_REGION) response = client.send_email( Source=source, Destination={ 'ToAddresses': [ to, ] }, Message={ 'Subject': { 'Data': subject, }, 'Body': { 'Text': { 'Data': body, } } } ) return response def lambda_handler(event, context): item = event['item'] # 対象テーブル table = dynamodb.Table('contacts') # 実行時間を登録しておく item['received_at'] = datetime.now().strftime("%Y/%m/%d %H:%M:%S") # テーブル登録 table.put_item( Item=item ) # メール送信 title = "お問い合わせが来ました" message = "会社: " + item['company_name']\ + "\n名前: " + item['name']\ + "\nメールアドレス: " + item['email']\ + "\n電話番号: " + item['tel']\ + "\n内容:" + item['content']\ src = "no-reply@test.com" dst = "admin@test.com" rsponse = send_email(src, dst, title, message) # 成功 return { 'statusCode': 200, 'body': json.dumps('success!') }
3. Lambdaでお問い合わせ内容をslackにも連携する
slack連携はwebhookを使用してPOSTすれば良いので urllib.request
でなげちゃいます。これだけのためにRequestsを入れるのは面倒そうだったのでurllibを使用しています。
先ほどのコードにさらに追記しちゃいます。
import json from datetime import datetime import boto3 from boto3.dynamodb.conditions import Key, Attr import urllib.request dynamodb = boto3.resource('dynamodb') MAIL_REGION = "us-east-1" def send_email(source, to, subject, body): client = boto3.client('ses', region_name=MAIL_REGION) response = client.send_email( Source=source, Destination={ 'ToAddresses': [ to, ] }, Message={ 'Subject': { 'Data': subject, }, 'Body': { 'Text': { 'Data': body, } } } ) return response def lambda_handler(event, context): item = event['item'] # 対象テーブル table = dynamodb.Table('contacts') # 実行時間を登録しておく item['received_at'] = datetime.now().strftime("%Y/%m/%d %H:%M:%S") # テーブル登録 table.put_item( Item=item ) # メール送信 title = "お問い合わせが来ました" message = "会社: " + item['company_name']\ + "\n名前: " + item['name']\ + "\nメールアドレス: " + item['email']\ + "\n電話番号: " + item['tel']\ + "\n内容:" + item['content']\ src = "no-reply@test.com" dst = "admin@test.com" rsponse = send_email(src, dst, title, message) # slack連携 url = 'https://hooks.slack.com/services/ slackから発行されたwebhooks url' # 自分のワークスペース,チャンネルのものに置き換えてください data = { 'text': '<!here>\nお問い合わせが来ました。\n *会社* \n' + item['company_name'] + '\n *名前* \n' + item['name'] + '\n *メールアドレス* \n' + item['email'] + '\n *電話番号* \n' + item['tel'] + '\n *内容* \n' + item['content'] } headers = { 'Content-Type': 'application/json' } req = urllib.request.Request(url, json.dumps(data).encode(), headers) with urllib.request.urlopen(req) as res: body = res.read() # 成功 return { 'statusCode': 200, 'body': json.dumps('success!') }
これで以下のようなメッセージが飛びます
@here お問い合わせが来ました。 *会社* テスト株式会社 *名前* テスト名前 *メールアドレス* hogehoge@test *電話番号* 000-0000-0000 *内容* 内容
最後に
例として挙げたソースコードは各機能をくっつけただけかつエラーハンドリングまともにしてないなど修正、リファクタリングする必要はありますが、ただ実装するだけなら簡単にできました。
メールの送信実行以外は lambda_handler
にほぼ処理を書いていてごちゃごちゃしてしまったのでこれから機能単位で分離していこうと思います。
また、実行しながら上記コードを書いているわけではないのでおかしいところなどありましたらご連絡していただけると助かります。