棺にはネギを散らして

香ばしく焼いて……

S3の静的環境で問い合わせフォームを作る(Vue CLIを使用)

現在、Amazon S3の静的サイトホスティングを使って、Vue CLI製のポートフォリオサイトを公開しています。
shiramiz.hatenablog.com


今回はその関連で、S3とVueを使って問い合わせフォームを作る方法についてまとめていきます。

全体設計

以下がポートフォリオサイトの全体像なのですが、今回ご紹介するのは赤枠の部分です。

f:id:shiramiz:20191107150933p:plain

行ったことを時系列にまとめると、

①Contactページ(フォーム)の制作
API Gateway&LambdaでPOST後の処理を設定
③SESでメールアドレスの用意、データはS3へ格納

調べながらだったのでもっと上手い進め方もあるかもですが、それぞれ順番にご紹介します。

①Contactページ(フォーム)の制作

Vueで作ると通信中のローディングアイコンの表示とかthanksページへの遷移とかめちゃくちゃ楽でした。

▽template部分

<template>
  <div>
      <p>お仕事のご依頼・その他問い合わせは以下よりご連絡ください。状況により、返信にはお時間を要する場合がございますので予めご了承ください。</p>
      <h2>問い合わせフォーム</h2>
      <div v-show="loading"><img src="@/assets/loading.svg" width="120" height="120"></div>
      <div v-show="thanks">送信が完了しました。</div>
    <form method="post" v-on:submit.prevent="onSubmit" autocomplete="off" v-show="showForm">
      <span class="required">入力項目はすべて必須です。</span>
            <div class="field">
                <input type="text" name="name" v-model="name" required>
                <label for="name">お名前</label>
                <span class="focus_line"></span>
            </div>
            <div class="field">
                <input type="text" name="email" v-model="email" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$" required>
                <label for="email">Email</label> 
                <span class="focus_line"></span>
            </div>
            <div class="field area">
                <textarea type="text" name="body" v-model="body" required rows="5"></textarea>
                <label for="title">本文</label> 
                <span class="focus_line"></span>
            </div>
            <span class="required" v-show="hasError">通信時にエラーが発生しました。<br>ページを更新して再度お試しください。</span>
            <div>
                <button type="submit">送信する</button>
            </div>
      </form>
  </div>
</template>

▽script部分

<script>
export default {
  name: 'Contact',
  data () {
    return {
      name:'',
      email:'',
      body:'',
      hasError:false,
      loading:false,
      showForm:true,
      thanks:false
    }
  },
  methods:{
      onSubmit:function(){
          var me = this;
          this.loading = true;
          this.showForm = false;
          axios.post('https://****.execute-api.us-east-1.amazonaws.com/v1/send', this.$data)
          .then(function (response) {
            me.thanks = true;
            me.loading = false;
          })
          .catch(function (error) {
            me.hasError = true;
            me.showForm = true;
            me.loading = false;
          });
      }
  }
}
</script>

CSSは省略します。

axiosを使ってdataをsubmitする

一般的なフォームと異なるのは、submit後の処理です。
axiosというライブラリを使ってデータをポストしています。以下の部分です。

methods:{
      onSubmit:function(){
          var me = this;
          this.loading = true;
          this.showForm = false;
          axios.post('https://****.execute-api.us-east-1.amazonaws.com/v1/send', this.$data)
          .then(function (response) {
            me.thanks = true;
            me.loading = false;
          })
          .catch(function (error) {
            me.hasError = true;
            me.showForm = true;
            me.loading = false;
          });
      }
  }

https://****.execute-api.us-east-1.amazonaws.com/v1/send
の部分には、POST先のURLを入力します。
後で出てくるAPI Gatewayの設定が済んだら明らかになるので、一旦仮の値を入れておきます。

まず、onSubmitで処理が呼び出され、以下の処理が走ります。

var me = this;
this.loading = true;
this.showForm = false;

これは送信ボタン押下後にフォームを非表示にしてローディングアニメーションを表示するためのものです。
template(html)側では以下のようにv-showを使って表示/非表示を切り替えています。

<div v-show="loading"><img src="@/assets/loading.svg" width="120" height="120"></div>
<form method="post" v-on:submit.prevent="onSubmit" autocomplete="off" v-show="showForm">

その後、axios.postが走り、成功した場合の処理を.thenで定義します。
通信が成功したら、ローディングアニメーションを非表示にし、thanksページを表示します。

me.thanks = true;
me.loading = false;

何らかエラーが発生した場合の処理は.catchで受け取ります。この場合はローディングアニメーションを非表示にし、エラー文とともにフォームを再表示します。

me.hasError = true;
me.showForm = true;
me.loading = false;

これらも同じくhtml側はv-showを使って表示/非表示を切り替えています。シンプル。

<span class="required" v-show="hasError">通信時にエラーが発生しました。<br>ページを更新して再度お試しください。</span>

②③POST~API Gateway&Lambdaでメール送信まで

②③に関しては以下の記事を参考にさせていただきました。
qiita.com

POSTされたデータを受け取るのはAPIGateWayです。それをトリガーにして、Lambdaの処理が起動するというフロー。Lambdaの処理は、受け取ったデータを問い合わせ者と自分にメール送信するのみにしています。
こまごまとした設定周りは割愛しますが、Lambda上のソースコードは以下のようにアレンジしました。

'use strict'
const SDK = require('aws-sdk');

exports.handler = (event, context, callback) => {
    const ses = new SDK.SES({ region: 'us-east-1' });
    const email = {
        // From
        Source: "mail@shiramiz.com",
        // To
        Destination: { ToAddresses: [ event.form.email ], CcAddresses: ['CCに入れて送りたいメールアドレス'] },
        Message: {
            // 件名
            Subject: { Data: "Webからのお問い合わせ" },
            // 本文
            Body: {
                Text: { Data: [
                    '以下の情報で問い合わせを受け付けました。',
                    '[名前] : ' + event.form.name,
                    '[メールアドレス] : ' + event.form.email,
                    '[お問い合わせ] : ' + "\n" + event.form.body,
                ].join("\n")}
            },
        },
    };
    ses.sendEmail(email, callback);
};