AWS Lambda( + APIGateway) を使ったAjaxなWebフォームを作ってみる

Jul 19, 2018 11:20 · 346 words · 2 minute read Tec AWS

今回は技術ネタです。一応エンジニアなので(^^:

今年になってAWSのLambda + APIGatewayを使って俗に言うサーバーレス開発ってのを結構やったんですが
Ajaxを利用する時にかなり苦戦を強いられました。
今回はその時に覚えた事を備忘録代わりに記事にしたいと思います。

概要

今回はありがちな入力フォームをAjaxで実装して見たいと思います。 フロントエンド側(html)とバックエンド側(サーバサイドなど)の連携で最も多いのはフォーム画面だと思います。 そして最近にWebフォームでAjaxを利用しないケースは少ないのでAjaxを利用します。

実装の流れ

  1. HTMLのフォームに名前とメールアドレスを入力しAPIGatewayへ
  2. APIGatewayが受け取りLambdaへフォーム内容を渡す
  3. Lambdaは受け取った名前やアドレスを扱ってなんらかの処理(例えばDB書き込みやメールを送るなどだがここの処理は割愛)
  4. Lambdaは通信結果をAPIGatewayへ
  5. HTMLはAjaxで結果を受け取る

図解するとこんな感じ

html<— API Gateway –> Lambda

作成手順

1.API Gateway:

Lambda関数を先に作作成時に自動生成だとメソッド: ANYになりCORSが有効にして効かない不具合が あるため、ANYを削除しPOSTで新たにメソッドを作成

  1. アクションのプルダウンメニューで以下の順に実行
  2. リソースのアクション-メソッドの作成
  3. POSTを選択

1-1.階層

階層は以下のようになっている想定

/
-/Test
--POST

1-2. セットアップ(POST - セットアップ)

  1. 統合タイプ-Lambda 関数を選択
  2. Lambda プロキシ統合の使用-今回はチェックを入れない
  3. Lambda リージョン-適切なものを選択
  • アジアパシフィック(東京)なのでap-northeast-1
  1. Lambda 関数
  • Test
  1. デフォルトタイムアウトの使用
  • チェック

1-3.CORSの有効化

アクションのプルダウンメニューで以下の順に実行

  1. リソースのアクション-CORSの有効化
  2. APIアクション-APIのデプロイ
  3. デプロイ

1-4.メソッドリクエスト

※ ここの設定が少々怪しいので要検証

–URL クエリ文字列パラメータ
—path
–HTTP リクエストヘッダー
—Content-Type

2-5.統合リクエスト

–本文マッピングテンプレート
—テンプレートが定義されていない場合 (推奨) をチェック
—Content-Type
—-application/jsonに
—–テテンプレート

※ここを適当にして本当にハマりました。

参考:API Gatewayへの入力値にLambdaからアクセスする こちらの記事が非常に参考

ここで渡ってくるデータを指定するので必ず設定が必要です。

テンプレートの例
{
  "accountId": "$context.identity.accountId",
  "apiId": "$context.apiId",
  "apiKey": "$context.identity.apiKey",
  "caller": "$context.identity.caller",
  "headers": {
#foreach( $key in $input.params().header.keySet() )
    "$key": "$input.params().header.get($key)"#if( $foreach.hasNext ),#end
#end
  },
  "httpMethod": "$context.httpMethod",
  "path": "$context.resourcePath",
  "pathParameters": {
#foreach( $key in $input.params().path.keySet() )
    "$key": "$input.params().path.get($key)"#if( $foreach.hasNext ),#end
#end
  },
  "queryParameters": {
#foreach( $key in $input.params().querystring.keySet() )
    "$key": "$input.params().querystring.get($key)"#if( $foreach.hasNext ),#end
#end
  },
  "requestId": "$context.requestId",
  "requestParameters": $input.json('$'),
  "resourceId": "$context.resourceId",
  "sourceIp": "$context.identity.sourceIp",
  "stage": "$context.stage",
  "user": "$context.identity.user",
  "userAgent": "$context.identity.userAgent",
  "userArn": "$context.identity.userArn"
}

2.Lambda関数:

設計図から[microservice-http-endpoint-python3]を選択 関数名は今回は「Test」とする

lambda_function.py(lambdaの関数)


import json
import urllib

def lambda_handler(event, context):
    #print(json.dumps(event, indent=4))

    try:
        # フォームに入力されたデータを得る
        param = urllib.parse.parse_qs(event['requestParameters'])
        username = param['username'][0]
        email = param['email'][0]
        
        # クライアントのIPを 得る
        host = event['sourceIp']
                
        # 結果を返す
        return {
            'statusCode' : 200,
            'headers' : {
                    'access-control-allow-origin' : '*',
                    'content-type' : 'application/json'
            },
            'data' : {'email' : email},
            'completed' : 1
        }

    except:
        import traceback
        traceback.print_exc()
        return {
            'statusCode' : 200,
            'headers' : {
                    'access-control-allow-origin' : '*',
                    'content-type' : 'application/json'
            },
            'completed' : 0
        }

3.HTML(フォーム)側

フォーム側のJavaScript(html)になります。
分かりやすくするためにhtmlの中でJavaScriptを書いています。
CSSなどは割愛しておりますのでご了承ください。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>jqueryのajaxのサンプル</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$( function() {

    var apiUrl = 'https://xxxxxxxxxxx/Test';

    $('#button').click(
        function(){
            var formData = $("#form").serialize();
            console.log("formData:" + formData);
            $.ajax({
                url:apiUrl, // 通信先のURL
                type:'POST',// 使用するHTTPメソッド (GET/ POST)
                data:formData, // 送信するデータ
                dataType:'json', // 応答のデータの種類 (xml/html/script/json/jsonp/text)
                contentType: "application/json",
                // 通信に成功した時に実行される
                }).done(function(response,textStatus,jqXHR) {
                    console.log("成功:" + jqXHR.status);

                    if (response.completed == 1)
                    {
                        $('#message').html('登録を完了しました。');
                        console.log(response.data.email);
                    }
                    else if(response.completed == 0)
                    {
                        $('#message').html('サーバーの内部エラーのため登録が失敗しました。');
                    }

                 // 通信に失敗した時に実行される
                }).fail(function(jqXHR, textStatus, errorThrown) {
                    alert("失敗:" + jqXHR.status);
                    console.log("失敗:" + jqXHR.status);
                });
        });
});
</script>
</head>
<body>
<form id="form" action="">
<input type="button" id="button" value="開始ボタン"><br/>
氏名<input type="text" name="username"><br />
メールアドレス<input type="text" name="email"><br />
</form>
<div id="message"></div>
</body>
</html>

おわりに

今回は簡単に試せるようにするためにjQueryを利用していますが、ReactやVeuなどかを利用する場合も応用が効くと思いますので是非ご利用ください。

また、今回の記事ですが

  • 不効率なやり方をしている
  • 間違っている部分がある
  • もっと良い方法がある

などなど、お気付きの点がありましたらこちらのお問い合わせからご連絡を頂けると助かります。

宜しくお願いします致します。

tweet Share