API Gateway+Lambda＋SESで簡易お問い合わせフォーム実装

<h1>はじめに</h1>
htmlフォームからPOST送信して、SESを使って、通知メールが飛び、リダイレクトページを返却するお問い合わせフォームを実装した。実装手順を忘れないように、備忘として残しておく。
※当初リターンメールを返す想定だったので、SESにしているが、SNSでもいい

<h1>HTMLの入力フォーム</h1>
値をPOSTして、API Gatewayに渡すためのサンプルフォーム
※API Gatewayのエンドポイントの部分は後ほど修正する必要あり

```html:sample.html
<!DOCTYPEhtml>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>お問い合わせフォーム</title>
</head>

<body>
<header>
    <h1>お問い合わせフォーム</h1>
</header>
<article>
    <form action="API Gatewayのエンドポイント" method="post" accept-charset="utf-8">
        <input type="text" name="name" placeholder="name"/>
        <input type="text" name="ruby" placeholder="ruby"/>
        <input type="email" name="email_address" placeholder="email_address"/>
        <input type="text" name="contents" placeholder="contents" maxlength='250'/>        
        <input type="submit" value="送信">
    </form>
</article>
</body>
</html>
```

<h1>Lambda</h1>
API Gatewayの実装の前に、Lambda関数を実装しておく。
・コンソール画面＞Lambda＞関数の作成
・関数名、関数で使う言語（今回はPython）、実行ロール（SESFullAccess、AWSLambda_FullAccess）を指定。今回はあらかじめ作っていたロールを付与しているが、新しいロール作成を作るもよし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/b61fb4fd-6d34-f0b8-a1de-8f0ce7a4ac84.png)

・作成したら、実際に関数の処理をコーディングする。
　最初はテンプレで、「Hello from Lambda!」を返すプログラムになっているので、以下に修正。

```py:sample.py
import boto3
import json
import urllib.parse

#定数(Lambdaの環境変数に設定しても良いが今回はベタがき)
FROM_MAIL = 'SESに認証させる送信元メールアドレス'
TO_MAIL = 'SESに認証させる送信先メールアドレス'
REGION = 'ap-northeast-1'
SUBJECT = '★要対応★お問い合わせメールへのご対応をお願いします'

#送信処理
def send_email(source, to, subject, body):
    client = boto3.client('ses', region_name=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):
    #eventの中の値をデコードして取得
    #API Gatewayがvalueをエンコードするため、デコードした値を代入する
    name = urllib.parse.unquote_plus(event['name'])
    ruby = urllib.parse.unquote_plus(event['ruby'])
    email_address = urllib.parse.unquote_plus(event['email_address'])
    contents = urllib.parse.unquote_plus(event['contents'])
    
    #固定メッセージ
    message = f'下記のお客様よりお問い合わせをいただきました。\n\n======================================\nお名前：{name}\nフリガナ：{ruby}\nメールアドレス：{email_address}\n問い合わせ内容：{contents}\n\n======================================\n\n※このメールはシステムから自動送信されています\n'

    r = send_email(FROM_MAIL, TO_MAIL, SUBJECT, message)
    
    #リダイレクト
    return {
        "statusCode": 302,
        "body": 'redirect',
        "Location": 'リダイレクト先.html'
    }

```

<h1>API Gateway</h1>
お問い合わせフォームからリクエストを受け取って、Lambdaに渡すためのAPI Gatewayを実装する。
・コンソール画面＞API Gateway＞APIの作成
・REST API＞構築
・API名を入力する画面になるので、TestAPIと記載して、他はデフォルトのまま、APIの作成
・アクション＞リソースの作成＞リソース名を記載＞リソースの作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/f7228c7d-50b7-3c2f-bf8d-29f18071bb2f.png)


・アクション＞メソッドの作成＞POST＞Lambda関数に先ほど作成した関数名入力＞保存
<b>※ちなみに、「Lambdaプロキシ統合の使用」にチェックを入れて、値をLambda関数に渡すことも可能だが、今回はマッピングテンプレートを使用する。
Lambdaプロキシ統合を使用する場合の参考記事は以下。（この記事では割愛）</b>
参考記事：https://cres-tech.hatenablog.com/entry/2020/10/03/112402 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/92fcaf6a-74e9-db96-d08d-19e9fe79b002.png)

・統合リクエスト＞マッピングテンプレートに以下を記載する。

```
{
    #foreach( $token in $input.path('$').split('&') )
        #set( $keyVal = $token.split('=') )
        #set( $keyValSize = $keyVal.size() )
        #if( $keyValSize >= 1 )
            #set( $key = $util.urlDecode($keyVal[0]) )
            #if( $keyValSize >= 2 )
                #set( $val = $keyVal[1] )
            #else
                #set( $val = '' )
            #end
            "$key": "$val"#if($foreach.hasNext),#end
        #end
    #end
}
```


<h3>リダイレクト</h3>
メール送信後にリダイレクトさせる場合には、もう1手順必要。
・メソッドレスポンス＞以下のように302リダイレクトステータスを指定＞200ステータスを削除する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/40bb94cb-b497-f400-37ca-3c597c630729.png)

・統合レスポンス＞302ステータスを追加する＞200ステータスを削除する
・ヘッダーのマッピング＞レスポンスヘッダーに「Location」と記載＞マッピングの値に「integration.response.body.Location」を指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/2777b024-5ccd-7208-d74e-3464a4bab19b.png)

・アクション＞デプロイ（デプロイステージは新しいステージでdevと記載）
・最初に作ったHTMLのURL呼び出し部分に以下のエンドポイントを指定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/a19af502-d8a8-e904-1ec0-031511d2eeee.png)


<h1>SES</h1>
今回は最初にリターンメールをメール送信者に返すつもりだったので、SESにしたが、社内通知メールならSNSでもいい。
・AWSコンソールからSES＞Email Addrees＞Verify a New Email Address
・送信元メールアドレスを指定する＞Verify This Email Address（送信先メールアドレスも同手順）
・AWSからメールが来るので、リンクを踏むと、Verification Statusがverifiedになる。（送信先メールアドレスも同手順）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1881328/a688b1c4-07a9-de1c-dfe9-c80100aa54bb.png)

<b>HTMLから値を入力して、認証先メールアドレスにメールが飛んで、リダイレクト先のページに遷移すれば、実装完了！</b>

<h1>その他</h1>
SESはデフォルトの設定だと認証されたアドレスにしかメールを送れない。問い合わせ者へのリターンメールを返す場合は、AWSにアカウントをサンドボックス外に移動してもらう申請をする必要がある。その場合の参考記事は以下。（この記事では割愛）
参考記事①：https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/request-production-access.html
参考記事②：https://bbh.bz/2019/08/20/ses-restriction-relaxation/
参考記事③：https://qiita.com/comefigo/items/454091c595aa56c43751