AWSLambdaだけでhtml入力フォームを作る

AWS Lambdaだけでhtmlフォームをつくる aws

AWS Lambdaを使用し、htmlで申請フォームを作成します。
htmlのformから、テキストなどをPOSTできるようになります。
マッピングテンプレートやS3は使用しません。SAMとLambdaを使用します。
使用言語は、Pythonです。

こんばんは、時雨風です。

最近、lambdaを使用して簡単な見える化システムを作りました。機械のステータスが表示されるものです。よくあるソーラーパネルで発電されている量が表示される看板のようなものと思っていただければ大丈夫です。

DynamoDBとlambda、S3とcloud frontを使用してバックエンドとフロントエンドを作成するところまでは順調でした。しかし、そこで問題が…

「お客さん(ご高齢の社長)が操作して確認できるようにしてくれ」

ふぁ!?

「送信はWeb APIで、curlを使って、パラメーターを設定してから送信してくれ」
なんて絶対に通じません。

そこで考えられたのが簡易的なhtml入力フォームからの送信でした。

一番大事なのは、"簡易的に"というところ。
だって、想定していない作業ですから残り時間がありません。

そして時雨風はせっせとhtmlフォームをつくりはじめるのでした…

はじまり、はじまり〜

こちらが完成したものになります。
解説では一部抜粋しているだけですので、こちらを見ると理解が深まります。プロジェクトのテンプレートとしても使えます。

GitHub - tokiukaze/lambda-html-form: lambdaでhtml formを作成するサンプルです。
lambdaでhtml formを作成するサンプルです。. Contribute to tokiukaze/lambda-html-form development by creating an account on GitHub.

使用するもの

ついついながながと書いてしまいましたが、前提知識のドキュメントを読めば大丈夫だと思います。
こだわりポイントとしては、入力フォームはhtmlのみでjsを使用しないこと、AWS API GatewayのマッピングテンプレートやS3を使用しないことです。あくまで"簡易的に"ですから、余計なものは削ぎ落とします。でもFaviconは必要です。はい。

PCにインストールされ、設定されている必要があるもの

VisualStudio Code
コードエディター

Python 拡張機能
vscodeのpython拡張プラグイン

AWS SAM CLI
AWS SAMをコマンドから操作するアプリケーション

使用ライブラリ

aws-lambda-powertools
lambdaで簡単な記述ができるようにするライブラリ

使用しないもの

AWS API Gateway マッピングテンプレート

S3

前提知識

AWSを操作するので、この二つの内容はある程度わかっている必要があります。

サーバーレスコードを実行する方法 – アマゾン ウェブ サービス (AWS)

クイックスタート: アプリケーションの公開 - AWS Serverless Application Repository

解説

ここから解説にうつりますが、まずはローカルで動かしたほうが理解しやすいと思うのでぜひやってみてください。

ローカルで動かす

サンプルプロジェクトをローカルで動かします。

gitaws sam cliのインストールが必要になります。

git clone https://github.com/tokiukaze/lambda-html-form.git
cd lambda-html-form

sam build && sam local start-api

すると下記の表示がでます。

Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

~~~~省略~~~~

その状態でブラウザからurlにアクセスします。
http://127.0.0.1:3000/form
下記のような画面がでます。

html form 画面
html フォーム

適当に入力し、「送信」をすると送信した内容がそのまま表示されます。

送信後に表示される
ブラウザに表示される

実装

ここからはソースコードの解説を行います。

まずはindex.htmlを作成します。
おもに使用するのは、inputタグです。

index.html 一部抜粋

<body>

    <form method="post">
        <input type="date" name="input1" />
        <br />
        <input type="checkbox" name="input2" />
        <br />
        <input type="text" name="input3" />
        <br />
        <input type="number" name="input4" />
        <br />
        <select name="pets" id="input5">
            <option value="">--Please choose an option--</option>
            <option value="dog">Dog</option>
            <option value="cat">Cat</option>
            <option value="hamster">Hamster</option>
            <option value="parrot">Parrot</option>
            <option value="spider">Spider</option>
            <option value="goldfish">Goldfish</option>
        </select>
        <br />

        <input type="submit" />
    </form>

</body>

上記のhtmlを配信するメソッドを定義します。
app.py

@app.get("/form")
def get_form() -> Response:
    """htmlを読み込み返却します。

    /index.htmlを読み込み返却しています。
    index.htmlには、入力フォームのサンプルが記述してあります。
    """

    html_path = Path("index.html")
    with open(html_path, "r") as f:
        file = f.read()

    return Response(status_code=200, content_type=content_types.TEXT_HTML, body=file)

formデータを受信するAPIを作成します。

@app.post("/form")
def post_form() -> Response:
    """htmlフォームからのデータを受信します。

    受信されたデータは解析したのち、webページに表示しています。
    """

    body_data: dict[str, Any] = html_form_split(app.current_event.body)

    # テキストが日本語の場合はURLデコードを行う
    body_data["input3"] = unquote(body_data.get("input3", ""))

    # dictをstrに変換する。日本語を含む想定のため、文字コードに注意する。
    res: str = json.dumps(body_data, ensure_ascii=False)

    # 日本語に対応するためcharsetを設定
    return Response(
        status_code=200,
        content_type=content_types.TEXT_PLAIN + "; charset=utf-8",
        body=res,
    )

def html_form_split(body: str) -> dict[str, Any]:
    """formから送信されたbodyをパースし、辞書型で返却します。

    Args:
        body (str): _description_

    Returns:
        dict[str, Any]: _description_
    """

    keys = []
    values = []

    for data in body.split("&"):
        pair = data.split("=")
        keys.append(pair[0])
        values.append(pair[1])

    return dict(zip(keys, values))

htmlからは、input1=value1&input2=value2のようなbodyが送信されます。

htmlのformの仕様については、下記をご覧ください。

フォームデータの送信 MDN

html_form_split関数を実装し、htmlから送信されるデータをパースし、使いやすいように辞書型にしています。
送信されたテキストに日本語が含まれると送信されたデータはURLエンコードされてしまいます。
URLデコードで文字に戻す必要があります。unquote()が該当します。
その後、テキストとしてformから送信されたデータを返却しています。

まとめ

lambdaのみでhtmlのformからデータを送信することができるようになりました。
会社には、「Reactは甘え。html/css/jsだけでサイトをつくるのが至高」という冗談を言う先輩がいますが、今回のような高度なことが必要とされないケースではその通りだと思います。

ここまで、お読みいただきありがとうございました。

コメント

タイトルとURLをコピーしました