AWS Lambdaを使用し、htmlで申請フォームを作成します。
htmlのformから、テキストなどをPOSTできるようになります。
マッピングテンプレートやS3は使用しません。SAMとLambdaを使用します。
使用言語は、Pythonです。
こんばんは、時雨風です。
最近、lambdaを使用して簡単な見える化システムを作りました。機械のステータスが表示されるものです。よくあるソーラーパネルで発電されている量が表示される看板のようなものと思っていただければ大丈夫です。
DynamoDBとlambda、S3とcloud frontを使用してバックエンドとフロントエンドを作成するところまでは順調でした。しかし、そこで問題が…
「お客さん(ご高齢の社長)が操作して確認できるようにしてくれ」
ふぁ!?
「送信はWeb APIで、curlを使って、パラメーターを設定してから送信してくれ」
なんて絶対に通じません。
そこで考えられたのが簡易的なhtml入力フォームからの送信でした。
一番大事なのは、"簡易的に"というところ。
だって、想定していない作業ですから残り時間がありません。
そして時雨風はせっせとhtmlフォームをつくりはじめるのでした…
はじまり、はじまり〜
使用するもの
ついついながながと書いてしまいましたが、前提知識のドキュメントを読めば大丈夫だと思います。
こだわりポイントとしては、入力フォームはhtmlのみでjsを使用しないこと、AWS API GatewayのマッピングテンプレートやS3を使用しないことです。あくまで"簡易的に"ですから、余計なものは削ぎ落とします。でもFaviconは必要です。はい。
PCにインストールされ、設定されている必要があるもの
VisualStudio Code
コードエディター
Python 拡張機能
vscodeのpython拡張プラグイン
AWS SAM CLI
AWS SAMをコマンドから操作するアプリケーション
使用ライブラリ
aws-lambda-powertools
lambdaで簡単な記述ができるようにするライブラリ
使用しないもの
前提知識
AWSを操作するので、この二つの内容はある程度わかっている必要があります。
サーバーレスコードを実行する方法 – アマゾン ウェブ サービス (AWS)
クイックスタート: アプリケーションの公開 - AWS Serverless Application Repository
解説
ここから解説にうつりますが、まずはローカルで動かしたほうが理解しやすいと思うのでぜひやってみてください。
ローカルで動かす
サンプルプロジェクトをローカルで動かします。
git
とaws 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
下記のような画面がでます。
適当に入力し、「送信」をすると送信した内容がそのまま表示されます。
実装
ここからはソースコードの解説を行います。
まずは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の仕様については、下記をご覧ください。
html_form_split
関数を実装し、htmlから送信されるデータをパースし、使いやすいように辞書型にしています。
送信されたテキストに日本語が含まれると送信されたデータはURLエンコードされてしまいます。
URLデコードで文字に戻す必要があります。unquote()
が該当します。
その後、テキストとしてformから送信されたデータを返却しています。
まとめ
lambdaのみでhtmlのformからデータを送信することができるようになりました。
会社には、「Reactは甘え。html/css/jsだけでサイトをつくるのが至高」という冗談を言う先輩がいますが、今回のような高度なことが必要とされないケースではその通りだと思います。
ここまで、お読みいただきありがとうございました。
コメント