できるだけPythonだけでWEBアプリを作る①(Python + AWS + HTML/JavaScript)
最近は生成AIを応用して色々と作ってみたりしていたのですが、著作権問題など色々な問題が話題になっているので、少し様子見で他のことを一度やってみようと思います。
今回は「WEBアプリを作ろう」ということで、作るまでの過程をまとめます。
ちなみに、私はPython以外の言語があまり扱えないので、Pythonを中心としてWEBアプリを作っています。
今回は初回ということで、簡単なものを作っており、次はもう少し複雑なものにもチャレンジしたいです。「作成したアプリ: タイヤサイズからタイヤ外径を計算するアプリ」
WEBアプリを作ろうとしたきっかけ
何か機能を作成した後、その機能をどなたかに使ってもらおうとすると、コードを渡して各々の環境で使ってもらうというやり方になるのかなと思うのですが・・・環境依存の問題があり、うまくいかないことも多いです。また、"不特定多数の人に気軽に使って貰えるものを作る"という目標達成のためにもWEBアプリを作れるようになることが大切かなと思っています。
目次
使用環境
・AWS Lambda ランタイム python3.9
・AWS API Gateway
・Amazon S3
フロント側はHTML/JavaScriptで構築し、バックエンド側はAWSで構築する
→複雑なところはAWS+Pythonでという戦略
(追記)アーキテクチャ

実装
前準備(AWS環境を整える)
AWS Lambda関数の作成
AWSコンソールにログインして、Lambdaの画面を開き、関数->関数作成を押下する。
以下のような画面が出るので、関数名は適当に記入、ランタイムは"Python3.9"を設定する。

API Gatewayの作成/設定
苦戦したので詳しく書きますが、まだ完全に理解できていないため、補足は追記予定。
Lambdaの関数画面で"トリガーを追加"個所をクリックして、ウィンドウを開く。
ウィンドウが開いたのち、"API Gateway"を選択すると以下画像のような設定画面が現れるので、APIタイプ REST API/セキュリティは"開く"を選択して設定する。

上記の通り作成すると、先程の関数ウィンドウのトリガー欄に、API Gatewayが作成される。

API Gatewayの名前をクリックして、詳細を設定する。
以下画像では、"Resources"欄に"/POST"という1つしか存在しないが、当初は"ANY"が存在している。
"ANY"はすべてのHTTPメソッドをサポートしており、色々使えそうであるが・・使い方が理解できていないので、一旦違う方法で対応。"ANY"メソッドは削除して"GET"メソッドを作成する。GETメソッドは、指定したリソースのデータを返すメソッドで、HTML上でJavaScriptを呼び出し、API Gateway経由でAWS Lambdaを呼び出してその値をJavaScriptに返す為に使用する。
作成画面は以下の通りで"Lambda 関数"と記載の個所は、自身の関数名を入力する

作ろうとしているシステムは、HTML/JavaScriptからのAWS API GatewayへのGETリクエストをAWS Lambdaに渡して、その結果を返すというもので、HTMLの入力値をLambdaに渡す必要があります。まずは、その渡す部分の設定をしていきます。
統合リクエストを選択して、"マッピングテンプレート(jsonパラメータ)の追加"を行う

json形式でAws lambdaに値を渡す為、Context-Typeにapplication/jsonを追加して、テンプレートを追加する。
{
"input1": "$input.params('param1')",
"input2": "$input.params('param2')",
"input3": "$input.params('param3')"
}
HTML/JavaScriptからは3つの変数(param1,param2,param3)が渡され、それらをlambdaに渡します。
lambda側からは、それぞれの変数をevent["param1"], event["param2"] , event["param3"] で取り出すことができます。
これに関しては理解が追いついておらず、詳しい説明は別サイトを参照した方が良い。
今回のプログラムを実行しようとするとブラウザのCORSというポリシーによるエラーが発生する。このエラーを回避するために、Aws lambdaから返ってきた値をHTML/javaに返す際に、アクセス許可をするためのヘッダーを追加する。
developer.mozilla.org
統合レスポンスを選択して、"レスポンスヘッダーの追加"を行う

以下の通り、"*"を入れることで、アクセス許可を付与

- API のデプロイ
"Resources"の横の"Actions"欄から、Deploy APIを選択することで、APIをデプロイ(設置)することができる。
URLに載っています。
デプロイ時のStage名などは、任意に設定する。
WEBページの作成/Amazon S3への保管
Amazon S3の設定について[省略:別ページで紹介]
HTMLのコード
HTMLの作成方法は以下を参考にして作成した。
基本的なフォーム -- ごく簡単なHTMLの説明
以下コードの"script src="の欄に、次で紹介するjavascriptのオブジェクトURL(S3に保管した)を入力する。コードはメモ帳などに入力後、"@@.html"の形式で保存しておく。
<!DOCTYPE html>
<heml lang="ja">
<head>
<meta charset="utf-8">
<title>Webアプリケーション sample1</title>
</head>
<body>
<article>
<h1>Webアプリケーション タイヤ外径計算機</h1>
<section>
<p>タイヤサイズ情報を入力すると、タイヤ外径値を返します</p>
<table>
<tr>
<th class="hissu"><label>タイヤサイズ</label></th>
<td><textarea id="input_textfield1" name="input" rows="2" cols="10"></textarea></td>
<th>/</th>
<td><textarea id="input_textfield2" name="input" rows="2" cols="10"></textarea></td>
<th>R</th>
<td><textarea id="input_textfield3" name="input" rows="2" cols="10"></textarea></td>
</tr>
</table>
<br>
<input type="submit" value="計算結果" onclick="send_message();">
<script src="<javaスクリプトのオブジェクトURLを記入>"></script>
<h2>出力結果</h2>
<table>
<tr>
<th id="output_label1">ここに出力されるよ</th>
<th>[mm]</th>
</tr>
</table>
<p></p>
</section>
</article>
</body>
</heml>
JavaScriptのコード
コードは以下を参考にして作成した。
JavaScriptでAPIを呼び出す方法を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン
以下コードの" request_url ="の欄に、AWS lambda 関数のオブジェクトURLを入力する。
コードはメモ帳などに入力後、"@@.js"の形式で保存しておく。
HTML/JavaScriptからAWS lambdaに変数を渡すことになるが、HTTPリクエストのパラメータリクエストは以下のように記載する。
↓HTTPリクエスト(パラメータあり)
https://(AWS lambda関数)?param1=値¶m2=値¶m3=値 #####"値"には実際に渡す引数値が入る。#####
コード
const send_message = () => {
// URLを作成
let input_label1 = document.getElementById("input_textfield1");
let input_label2 = document.getElementById("input_textfield2");
let input_label3 = document.getElementById("input_textfield3");
var parameter1 = input_label1.value;
var parameter2 = input_label2.value;
var parameter3 = input_label3.value;
parameter1 = parameter1.replace(/\r?\n/g, '\\r\\n'); // 改行コードを入れるとAWSでの処理が怪しかったので、文字列に置換している(TODO:改善)
parameter2 = parameter2.replace(/\r?\n/g, '\\r\\n'); // 改行コードを入れるとAWSでの処理が怪しかったので、文字列に置換している(TODO:改善)
parameter3 = parameter3.replace(/\r?\n/g, '\\r\\n'); // 改行コードを入れるとAWSでの処理が怪しかったので、文字列に置換している(TODO:改善)
parameter1 = encodeURI(parameter1);
parameter2 = encodeURI(parameter2);
parameter3 = encodeURI(parameter3);
console.log(parameter1);
console.log(parameter2);
console.log(parameter3);
request_url = "<AWS lambda関数のオブジェクトURLを記入>?";
request_url = request_url + "param1=" + parameter1 + "&" + "param2=" + parameter2 + "&" + "param3=" + parameter3;
console.log(request_url);
// リクエストオブジェクトの作成"
var request = new XMLHttpRequest();
request.open('GET', request_url, true);
// リクエストが成功したときに呼ばれる関数
request.onload = function () {
var json_data = this.response;
var return_message1 = JSON.parse(json_data)["body"];
let output_label1 = document.getElementById("output_label1");
output_label1.innerText = return_message1;
};
request.send(); // URLリクエストを送信する
}
AWS lambdaから返ってきた値は、以下の通りに受け取って、HTMLの更新を行う。
// リクエストが成功したときに呼ばれる関数
request.onload = function () {
var json_data = this.response;
var return_message1 = JSON.parse(json_data)["body"];
let output_label1 = document.getElementById("output_label1");
output_label1.innerText = return_message1;
};
Amazon S3にウェブページを保管
WEBページHTML/JavaScriptをそれぞれS3に保存する。
HTMLファイルのオブジェクトURLがWEBアプリのアクセスURLとなる
フロントエンド部分をHTMLで構成している為
AWS Lambdaでコード実行(動作確認)
コード作成
Lambda関数では、以下コードを作成/実行する。
(こちらがフルのコードとなります)
import json
def lambda_handler(event, context):
# TODO implement
param1 = event['input1']
param2 = event['input2']
param3 = event['input3']
try:
if str.isnumeric(param1):
overall_diameter_of_tyre = float(param1)*float(param2)/100*2+float(param3)*25.4
return {
'statusCode': json.dumps(200),
'body': json.dumps(overall_diameter_of_tyre)
}
except Exception as e:
raise e
return {
'statusCode': json.dumps(400),
'body': json.dumps("Error")
}
コード解説
HTML/JavaScriptからは3つの変数(param1,param2,param3)が渡され、それらをlambdaに渡します。
lambda側からは、それぞれの変数をevent["param1"], event["param2"] , event["param3"] で取り出すことができます。
param1 = event['input1'] param2 = event['input2'] param3 = event['input3']
lambdaで処理した結果は、関数:lambda_handlerの戻り値でjson形式で返す
return {
'statusCode': json.dumps(400),
'body': json.dumps("Error")
}JavaScript側の値の受け取り方は、上の記述の通り
実行結果
値を入力した後に"計算結果"ボタンを押すと、出力結果が表示されました。
