AWS Lambda、API Gateway、SESを使って静的サイト化からメール送信フォームを作成!

AWS
この記事は約11分で読めます。

こんにちは、@Manabu です。

Webアプリケーションを開発する際、ユーザーからの問い合わせやフィードバックを受け取るためのメール送信フォームは非常に重要です。

PHPなどで開発している場合、サーバー側の処理でメールを送信させることは簡単に実装できますが、HTMLなどの静的ページで構成されているサイトでは、構成を考える必要があります。

今回は、静的ページでメール送信フォームを作成するために、AWSのLambda・API Gateway・SESを使用して、機能を実装したいと思います。

この記事は、以下のような方におすすめです。

・AWSで開発している
・静的サイトでメール送信フォームを実装したい

ぜひ、参考にしてください。

はじめに

今回使用するAWSのサービス・構成について簡単に紹介します。

使用するサービス

AWS Lambda:サーバーレスコンピューティングサービス。コードを実行するためにサーバーを管理する必要がありません。イベントに応じて自動的にスケーリングします。

API Gateway:サーバーレスAPIの作成と管理を容易にするサービス。RESTful APIやWebSocket APIを簡単に作成できます。

AWS SES (Simple Email Service): 信頼性の高いスケーラブルなクラウドベースのメールサービス。大量のメール送信、受信、およびトランザクションメールを簡単に管理できます。

SESは、IDの検証を行う必要があるため、以下の記事を参考にIDの作成をお願いします。

簡単手順!独自ドメインでAWS SESを使ってメールを送信してみた

構成

完成形の構成は以下のようなイメージです。

S3で管理している静的サイトから、API Gateway経由でLambdaを起動し、SESでユーザーにメールを送信します。

なぜフロント側でメールを送信しないのか

フロント側(JavaScript)でメールを送信することもできますが、バックエンド側でメールを送信する理由について説明します。

セキュリティの問題

フロントエンドでメール送信サービス(例:SMTPサーバーや外部メールサービス)を使用するためのAPIキーや認証情報を保持すると、それらが簡単に漏洩する危険があります。ブラウザのデベロッパーツールを使えば、これらの情報を容易に取得できてしまうため、非常に危険です。

バックエンドでメール送信を行うことで、APIキーや認証情報を安全に保管し、外部に漏洩するリスクを減らすことができます。

制約事項

多くのブラウザは、セキュリティ上の理由から直接メール送信プロトコル(SMTPなど)をサポートしていません。JavaScriptや他のフロントエンド技術のみで信頼性のあるメール送信を実現することは非常に難しいです。

また、フロントエンドでのデータ検証や処理は限定的であり、サーバーサイドのような複雑なロジックを実装するのは難しいです。データの整合性やセキュリティを保つためには、サーバーサイドでの処理が必要です。

信頼性の向上

バックエンドでメール送信をキューイングすることで、同時に大量のメールを送信する場合でもシステムに負荷をかけずに処理できます。

また、負荷分散を行うことで、システム全体の安定性を保つことができます。

これらの理由から、フロント側ではなくバックエンド側でメールの送信処理を実装しています。

では、機能の作成に進んでいきましょう。

AWS Lambda関数の作成

AWSのマネジメントコンソールより、Lambda関数を作成します。

関数名を入力し、ランタイムでPythonを選択したら作成を行います。

関数を作成すると、Lambdaで使用する実行ロールも作成されています。

初期状態だと、SESでの実行権限がないため、権限を追加します。
※今回は、SESのフルアクセス権限を許可していますが、SESでのメールの送信だけなら「ses:SendEmail」アクションだけ許可すれば良いので、ご自身で判断してください。

作成した関数には、以下のコードを貼り付けてデプロイを行います。

import json
import boto3
from botocore.exceptions import ClientError

ses = boto3.client('ses', region_name='ap-northeast-1') # リージョンを確認してください

def lambda_handler(event, context):
    if event['httpMethod'] == 'OPTIONS':
    return {
        'statusCode': 200,
        'headers': {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'POST,OPTIONS'
        },
        'body': json.dumps({'message': 'CORS preflight request successful'})
    }

    body = json.loads(event['body'])

    email_subject = body['subject']

    try:
        ses.send_email(
            Source='test@it-slroom.blog', # 送信元の検証済みメールアドレス
            Destination={
                'ToAddresses': [ body['email'] ] # 送信先のメールアドレス
            },
            Message={
                'Subject': {
                     'Data': email_subject,
                },
                'Body': {
                    'Text': {
                        'Data': body['message'],
                     },
                 },
            },
        )
    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Methods': 'POST,OPTIONS'
        },
        'body': json.dumps({'message': 'メールが送信されました!'})
    }
    except ClientError as e:
    return {
        'statusCode': 500,
        'headers': {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Methods': 'POST,OPTIONS'
        },
        'body': json.dumps({'message': 'メールの送信に失敗しました。', 'error': str(e)})
    }

関数の作成ができたので、テストしてみます。入力値は以下で試してみてください。

{
    "httpMethod": "POST",
    "body": "{\"name\":\"名前\",\"email\":\"送信先のメールアドレス\",\"subject\":\"SES送信テスト\",\"message\":\"メールの本文になります。\"}"
}

テストを行なって問題なければ送信先のメールアドレスにメールが届いているか確認しましょう。

ここまででLambda関数の作成は完了です。

API Gatewayの作成

続いて、LambdaのトリガーとなるAPI Gatewayの作成を行います。

Lambdaのコンソールから、トリガーを追加を選択し、表示された画面でAPI Gatewayを選択して、APIタイプでHTTPを選択します。

作成したら、設定を追加します。

CORS(Cross-Origin Resource Sharing)は、Webブラウザがあるオリジン(ドメイン、プロトコル、ポート)から別のオリジンへのリソースアクセスを制御するための仕組みです。

CORSの設定を以下のように追加します。

Access-Control-Allow-Origin:*
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:*

オリジンやヘッダーは、もう少し制限してもいいので、個別で修正するようにお願いします。

静的ページの用意

メール送信用フォームを静的ページに用意します。

以下のコードを参考に作成して下さい。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>メール送信フォーム</title>
</head>
<body>

  <h1>メール送信フォーム</h1>
  <form id="emailForm" method="post">
    <label for="name">名前:</label><br>
    <input type="text" id="name" name="name" required><br><br>
    <label for="email">メールアドレス:</label><br>
    <input type="email" id="email" name="email" required><br><br>

    <label for="subject">件名:</label><br>
    <input type="text" id="subject" name="subject" required><br><br>

    <label for="message">本文:</label><br>
    <textarea id="message" name="message" rows="10" cols="30" required></textarea><br><br>

    <input type="submit" value="送信">
  </form>

<script>
  document.getElementById('emailForm').addEventListener('submit', function(event) {
    event.preventDefault();

    const formData = new FormData(event.target);
    const jsonData = JSON.stringify(Object.fromEntries(formData));

    fetch("https:APIGateway/URL", { // API GatewayのURLに書き換えてください
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: jsonData
    })
    .then(response => response.json())
    .then(data => alert('メールが送信されました!'))
    .catch(error => console.error('エラー:', error));
  });
</script>
</body>
</html>

表示された画面の入力欄に値を入力して、送信ボタンを押すことで、API GatewayからLambdaを起動しSESからメールを送信することができます。

まとめ

今回は、静的サイトからSESを使用して、メールを送信する方法について紹介しました。

S3の静的ウェブホスティングでサイトを公開されている方にとっては、非常に有益な情報だと思うので、ぜひ参考にしてください。