話題の記事

S3 静的ウェブサイトにサーバーレスなお問い合わせフォームを実装してみた(Amazon SES + AWS Lambda + API Gateway)

この記事では S3 の静的ウェブページにお問い合わせメールフォームを実装する手順をご紹介しています。
2021.11.17

はじめに

みんなが大好きな Amazon S3 の「静的ウェブサイトホスティング」で公開したウェブサイトに、メールフォーム付きのお問い合わせページが欲しくなるケースも多いと思います。

そこで今回は AWS のクラウドサービスをフル活用し、完全にサーバーレスで動作するメールフォームを構築してみました。

1時間ほどの作業でお問い合わせフォームを実装でき、AWS に触れることで「サーバーレス構成」の基本を理解するのにも役立ったので、備忘を兼ねて構築方法をご紹介します!

今回の構成(概略図)

サーバーレスだと何が嬉しいの?

おサイフに優しい

メール送信のバックエンドに利用するAWSサービス(Amazon SES、Lambda、API Gateway)はいずれも使用量に応じた従量課金制なので、常時メールサーバーを運用する場合と比較し、不要な固定料金が発生しないのが嬉しいポイントです!

メールサーバー要らずで管理が楽

メール送信に AWS のマネージドサービス Amazon SES を利用しているため、自前でメールサーバーを構築してメンテナンスして、スパムメール扱いされない対策もして… といった管理の手間を省略できるのも大きなメリットです。

カスタマイズが自由

メールフォームのフロントエンド部分は Amazon S3 の静的ウェブサイトで構築するため、バックエンドとは完全に分離されています。
そのため既存の静的ウェブサイトに組み込むことが容易で、使い慣れた Javascript や CSS で自由にカスタマイズできます。もちろん Lambda 関数でバックエンドの動作をカスタマイズすることも自由です!

やってみた

AWS のアカウントはすでに取得済みの前提で、以下の手順を実施していきます!

手順1. Amazon SES で送信先メールアドレスを登録する

1-01.
まずはメールフォームの送信先となるメールアドレスを用意しましょう。

Amazon SES のコンソールの「Verified identities」で[Create identity]ボタンをクリックします。

1-02.
今回は管理者用メールアドレスのみへの送信を許可するため Identity type では「Email address」を選択し、Email address の欄に目的のメールアドレスを入力して[Create identity]ボタンをクリックします。
※ 登録した以外へのメールアドレスへ送信したい場合はサンドボックスの解除申請が必要になりますが、ここでは説明を割愛します。

1-03.
入力したメールアドレス宛てに、下記のようなメールが届いているのを確認します。
(届かない場合は、迷惑メールボックス等を確認してみてください)

メール本文内の承認リンクをクリックします。
※「Amazon SES の特徴」のページが開きますが、特に何もせず閉じてOKです。

1-04.
SES コンソールの「Verified identities」画面に戻り、登録したメールアドレスの Status が「Verified」になっていれば登録完了です。

これで送信先メールアドレスの準備は完了です。
次は SES を利用してメール送信を行う Lambda 関数を作成していきます。

手順2. メールを送信する Lambda 関数の作成

2-01.
まず前準備として、Lambda 関数から SES サービスを操作するための IAM ロールを作成します。

IAM コンソールの「ロール」ページから「ロールの作成」をクリックします。

2-02.
信頼されたエンティティの種類を選択で「AWSサービス」を選択し、ユースケースの選択では「Lambda」を選択して次のステップへ進みます。

2-03.
Attach アクセス権限ポリシーでは「AWSLambdaBasicExecutionRole」と「AmazonSESFullAccess」を検索して選択し、次のステップへ進みます。

次の画面の「タグの追加」は任意のため、特に必要なければスキップして次のステップへ進みます。

2-04.
「確認」の画面では、ロール名や説明を設定します。
ロール名は任意ですが、ここでは「LambdaSESMailRole」とし、説明はデフォルトのままで[ロールの作成]ボタンをクリックします。

以上で IAM ロールの作成は完了です。
Lambda 関数の作成時にこの IAM ロールを指定するため、ロール名を控えておきましょう。

2-05.
ここから Lambda 関数を作成していきます。 AWS Lambda のコンソールで「関数」ページへ移動し「関数の作成」ボタンをクリックします。

2-06.
「一から作成」を選択し、任意の関数名を指定します。
ここでは関数名を「send-mail-SES」とし、ランタイムは「Node.js 14.x」を選択しています。

「デフォルトの実行ロールの変更」で「既存のロールを使用する」を選択し、先ほど作成した IAM ロール(LambdaSESMailRole)を指定します。

「詳細設定」は特に変更せず[関数の作成]ボタンをクリックします。

2-07.
関数コードの編集画面に遷移します。
下記のコードの必要部分を編集して貼り付け、[Deploy]ボタンをクリックしてデプロイしましょう。

Lambda 関数「send-mail-SES」の内容

  • メールアドレスの部分には、手順1で SES に登録したメールアドレスを記述しましょう。 (送信元や送信先に他のメールアドレスを指定したい場合は、SES に登録する必要があります)
  • 下記は東京リージョンのSESを使用する例です。他のリージョンを使用する場合は、5行目のリージョン名の記述を変更しましょう。
  • 「Body」の部分は送信するメールの内容を定義します。この部分を変更したい場合は、後工程の API Gateway やメールフォームの HTML の内容も合わせて変更する必要があります。
'use strict'
const SDK = require('aws-sdk');

exports.handler = (event, context, callback) => {
    const ses = new SDK.SES({ region: 'ap-northeast-1' });
    const adminaddress = 'SESで登録したメールアドレス'
    const email = {
        Source: 'SESで登録したメールアドレス',
        Destination: { 
            ToAddresses: [ adminaddress ]            
        },
        Message: {
            Subject: { Data: "フォームからのお問い合わせ" },
            Body: {
                Text: { Data: [
                    '[お問い合わせ表題] : ' + event.form['subject'],
                    '[メールアドレス] : ' + event.form['email'],
                    '[お問い合わせ本文] : ' + "\n" + event.form['body']
                ].join("\n")}
            },
        },
    };
    ses.sendEmail(email, callback);
};

これで Lambda 関数の作成は完成です。
次はお問い合わせフォームからリクエストを受け取って Lambda 関数へデータを渡す API を、 API Gateway で作成していきます。

手順3. リクエストを受け取る API の作成

3-01.
API Gateway のコンソールの API タイプ選択で「REST API」を選択し[構築]ボタンをクリックします。

3-02.
「最初のAPIを作成する」というモーダル画面が出ますが「OK」をクリックして閉じます。

「REST」「新しい API」を選択します。
「API名」は任意ですが、ここでは「sendMailAPI」とします。
エンドポイントタイプは「リージョン」とし、[APIの作成]ボタンをクリックします。

3-03.
リソースの作成画面に遷移します。アクションメニューから「リソースの作成」を選択します。
リソース名を「send」とし、「API Gateway CORS を有効にする」にチェックを入れて、画面下部の[リソースの作成]ボタンをクリックします。

3-04.
次にアクションメニューから「メソッドの作成」を選択し、プルダウンメニューで「POST」を選択して、横のチェックマークをクリックして POST メソッドを保存します。

3-05.
POSTメソッドの設定画面で統合タイプを「Lambda関数」にし、「Lambdaリージョン」「Lambda関数」には前項で作成した Lambda 関数「send-mail-SES」のリージョンと関数名を入力して[保存]ボタンをクリックします。
※ 「Lambda 関数がxxリージョンにありません」というようなメッセージが出ることがありますが、Lambda 関数名をコピーしてきて貼り付ければOKです。

「API Gateway に、Lambda 関数を呼び出す権限を与えようとしています」というメッセージが出ますが「OK」をクリックして先に進みましょう。

3-06.
POSTメソッドの「統合リクエスト」をクリックし、マッピングテンプレートの項目から「マッピングテンプレートの追加」をクリックし、Content-Type に「application/json」と入力して、横のチェックマークをクリックして保存します。

テンプレートの内容には下記のコードを貼り付けて「保存」ボタンをクリックします。

マッピングテンプレートに入力する内容

各項目名は、手順 2-07. で作成した Lambda 関数内の「Body」の設定値と一致させましょう。

{
    "form": {
        "subject":  "$util.escapeJavaScript($input.path('$.subject'))",
        "email":  "$util.escapeJavaScript($input.path('$.email'))",
        "body":  "$util.escapeJavaScript($input.path('$.body'))"
    }
}

3-07.
このあとの手順で、HTML のお問い合わせフォームから Javascript を使用してAPIを呼び出しますが、この時に「CORS エラー」となるのを回避するため、POSTメソッドの CORS 設定もしておきます。

※ CORS とは何かについては「REST API リソースの CORS を有効にする」のドキュメントをご一読ください。

POSTメソッドの「メソッドレスポンス」で200のレスポンスヘッダーに「Access-Control-Allow-Origin」を追加します。

続いてPOST メソッドの「統合レスポンス」で、ヘッダーのマッピングに、以下の設定を追加します。

  • レスポンスヘッダー:「Access-Control-Allow-Origin
  • マッピングの値:「'*'

3-08.
ここまでの設定が完了したら、API をデプロイします。
「アクション」から「APIのデプロイ」をクリックします。

「デプロイされるステージ」は任意ですが、ここでは「v1」とします。
任意の説明を入力して「デプロイ」をクリックします。

3-09.
デプロイしたAPIのURLが表示されます。
このあとのHTML作成時に使用するので、URLをコピーしておきましょう。
※今回の場合はこのURLの末尾に「/send」を付加したものがAPIのURLになります。

以上にて、API のデプロイは完了です。
次は、この API へお問い合わせを送信するフォームの HTML ページを作成していきます!

手順4. お問い合わせフォームの HTML ファイル作成

4-01.
今回使用するファイルは「index.html」と「function.js」の2つのみです。
下記のコードをコピーペーストして、2つのファイルを作成しましょう。

index.html の内容

ここまでの手順で、メールの項目(subject,email,body 等)を変更している場合は、下記の内容もそれに合わせましょう。

<!DOCTYPE html>
<html>
<head>
    <title>サーバーレスお問い合わせフォーム テスト</title>
    <meta charset=utf-8>
    <meta name=viewport content="width=device-width, initial-scale=1">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css" rel=stylesheet />
</head> 
<body>
    <div class="container" style="max-width:960px; margin:60px;">
        <h2 class="title is-4">お問い合わせフォーム</h2>
        <form class=field method=post id=mailForm> 
            <input class=input name=subject placeholder="お問い合わせの表題"> 
            <input class=input name=email placeholder="メールアドレス"> 
            <textarea class=textarea placeholder="お問い合わせ本文" name=body></textarea> 
            <div><button class="button is-midium is-link center" id=button type=submit>送信する</button></div> 
        </form>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.19.1/dist/jquery.validate.min.js"></script> 
    <script src="function.js"></script> 
</body> 
</html>

function.js の内容

  • [作成したAPIのURL]の部分には、手順3-09で作成した API のURLに「/send」を付加したものを記述しましょう。
  • メール送信成功・失敗時の処理や、フォームの入力チェック部分は任意に変更してOKです。
$(function(){
  ///// Eメールの送信処理
  $.validator.setDefaults({
    submitHandler: function() {
      $('form').on("submit", function(){
        $('#button').text('送信中です');
        var data = $('form').serializeArray();
        data = parseJson(data);

        $.ajax({
          url:           '[APIのURL]',
          type:          'post',
          dataType:      'json',
          contentType:   'application/json',
          scriptCharset: 'utf-8',
          data:          JSON.stringify(data)
        })
        .then(
          function (data) {
            ///// 送信成功時の処理
            alert('送信に成功しました');
            location.reload();
          },
          function (data) {
            ///// 送信失敗時の処理
            alert('送信に失敗しました');
            location.reload();
        });    
      })
      var parseJson = function(data) {
        var returnJson = {};
        for (idx = 0; idx < data.length; idx++) {
          returnJson[data[idx].name] = data[idx].value
        }
        return returnJson;
      }

    }
  });
  ///// フォームの入力チェック
  $("#mailForm").validate({
    errorElement: "span",
    errorClass: "alert",
    rules: {
      'subject': {
          required: true,
          maxlength: 50
      },
      'email': {
          required: true,
          email: true
      },
      'body': {
          required: true
      }
    },
    messages: {
      'subject': {
          required: "表題を入力してください",
          maxlength: "表題は50文字以内にしてください"
      },
      'email': {
          required: "メールアドレスを入力してください",
          email: "有効なメールアドレスを入力してください"
      },
      'body': {
        required: "本文を入力してください"
      }
    }
  });
});

これで HTML ファイルの作成は完了です。
最後に S3バケットを作成し、作成した HTML ファイルを静的ウェブサイトホスティングで Web 上に公開しましょう!

手順5. S3 バケットの作成とウェブページの公開

5-01.
AWS のコンソールから、新規 S3 バケットを作成します。

今回は特にドメイン名の紐づけは行わないため、バケット名は任意でOKです。
このバケットで静的ウェブサイトを公開するため「パブリックアクセスをすべて ブロック」のチェックは外し、承認にチェックを入れます。

他の部分は特に変更せず[バケットを作成]をクリックします。

これでバケットの作成は完了です。

5-02.
前項で作成したHTMLファイルのアップロードを行っていきます。

作成したバケットの設定画面で[アップロード]をクリックし、先ほど作成した「index.html」と「function.js」を追加します。

静的ウェブページとして公開するため「パブリック読み取りアクセス権の付与」を選択し、承認にチェックを入れます。

他の部分は特に変更せず[アップロード]ボタンをクリックします。

これでファイルのアップロードは完了です。

5-03.
S3 バケットで、静的ウェブサイトホスティングを有効化します。

バケットの「プロパティ」のタブで「静的ウェブサイトホスティング」の[編集]をクリックします。

静的ウェブサイトホスティングを「有効にする」、ホスティングタイプを「静的ウェブサイトをホストする」、インデックスドキュメントを「index.html」として[変更の保存]ボタンをクリックします。

静的ウェブサイトホスティングが有効になったら「バケットウェブサイトエンドポイント」のURLをクリックしましょう。

これで先ほどアップロードした「index.html」ファイルが表示されたら、お問い合わせフォームページの公開は完了です!

5-04.
公開したお問い合わせフォームの動作確認をしましょう。
各項目を入力し[送信する]ボタンをクリックします。

ここまでの設定に問題なければ「送信に成功しました」とアラートが表示されるはずです。 SES で登録した送信先メールアドレスに、メールが届いていることも確認してみましょう。

これで、S3 静的ウェブサイトにサーバーレスのお問い合わせフォームを実装できました。
お疲れさまでした!

おわりに

今回は最小限の手順のみをご紹介していますが、色々工夫をすればもっと実用的なお問い合わせフォームになると思います。
執筆者の前回の記事では S3 の静的ウェブサイトに投稿型コンテンツを実装する方法をご紹介していますので、併せてご覧いただければ幸いです!

参考資料

Lambda と Amazon SES を使用して E メールを送信する
Amazon S3 を使用して静的ウェブサイトをホスティングする
Amazon API Gateway で AWS Lambda を使用する
jQuery Validation Plugin
Bulma: Free, open source, and modern CSS framework based on Flexbox