😌

Next.jsとAPI Gateway連携済みのLambda(Python)を使用して簡単なパスワード検証を実装する

2024/02/02に公開

はじめに

完成イメージ

フロントエンドの環境は、下記の記事で作成した基本的な環境を使用しています。
https://zenn.dev/nenenemo/articles/082ac7dcffe308

フロントエンド

page
'use client';

import { useState } from 'react';
import styles from './page.module.css';

export default function Home() {
  const [inputPassword, setInputPassword] = useState('');
  const [message, setMessage] = useState('');

  const endPoint = process.env.NEXT_PUBLIC_LAMBDA_ENDPOINT || '';

  const handleInputChange = (e: any) => {
    setInputPassword(e.target.value);
  };

  const postButton = async () => {
    try {
      const requestBody = JSON.stringify({ password: inputPassword });
      const response = await fetch(endPoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: requestBody,
      });

      const responseData = await response.json();

      if (response.ok) {
        setMessage(JSON.parse(responseData.body).message);
      } else {
        console.error('HTTP Error:', response.status);
        setMessage('リクエストが失敗しました');
      }
    } catch (error) {
      console.error('エラー:', error);
      setMessage('エラーが発生しました');
    }
  };

  return (
    <main className={styles.main}>
      <div>
        <input
          type='password'
          value={inputPassword}
          onChange={handleInputChange}
        />
        <button onClick={postButton}>パスワードを送信</button>
        <p>{message}</p>
      </div>
    </main>
  );
}

Lambda関数の作成

今回はPythonを使用するので下記のように選択します。

関数名を入力、
ランタイムはPython 3.12
アーキテクチャはx86_64を選択して、関数の作成を選択ください。

コードソースには以下を入力して、Deployを選択してください。

lambda_function.py
import json
import traceback
import logging

# ログの設定
logger = logging.getLogger(__name__)  # loggerオブジェクトの初期化
logger.setLevel(logging.ERROR)  # エラーレベル以上のログを記録

def lambda_handler(event, context):
    try:
        if 'body' not in event or not event['body']:
            return {
                "statusCode": 400,
                "headers": {"Content-Type": "application/json; charset=utf-8"},
                "body": json.dumps({"message": "リクエストボディがありません"})
            }

        if isinstance(event['body'], str):
            request_body = json.loads(event['body'])
        else:  # 'body'が既に辞書型(dict)の場合はそのまま使用
            request_body = event['body']

        input_password = request_body.get('password')

        if input_password == 'a':
            response_body = {"message": "パスワードは正しいです"}
            response_status_code = 200
        else:
            response_body = {"message": "パスワードが正しくありません"}
            response_status_code = 401

        return {
            "statusCode": response_status_code,
            "headers": {"Content-Type": "application/json; charset=utf-8"},
            "body": json.dumps(response_body, ensure_ascii=False, indent=2)
        }

    except Exception as e:
        logger.error("エラーメッセージ: %s", str(e))
        logger.error("トレースバック情報: %s", traceback.format_exc())

        return {
            "statusCode": 500,
            "headers": {"Content-Type": "application/json; charset=utf-8"},
            "body": json.dumps({"message": "内部サーバーエラーが発生しました"}, ensure_ascii=False, indent=2)
        }

テストの作成

テスト > テストイベント
イベント名を入力、イベント JSONには以下を入力して、

{
  "body": "{\"password\":\"a\"}"
}

保存を選択してください。

テストの実行

テストを選択してください。
以下のように表示されるはずです。

API Gatewayの作成

API タイプを選択 > REST APIの構築を選択

API名を入力してAPIを作成を選択してください。

メソッドの作成

メソッドを作成を選択

Lambda プロキシ統合がオフになっていることを確認して

メソッドタイプはPOST,
統合タイプはLambdaを選択、
Lambda関数を選択するか黒枠の部分にARNを入力して、メソッドを作成を選択してください。
メソッドが実行された際に実行されるバックエンドのコードとして、Lambda関数を選択しています。このLambda関数がAPI Gatewayから呼び出され、リクエストの処理とレスポンスの生成を行います。

Lambda関数のARNは黒塗りの箇所になります。

マッピングテンプレートの追加

メソッドを作成し画面が切り替わったら、POSTを選択、

統合リクエストを選択

編集を選択

リクエスト本文のパススルーはテンプレートが定義されていない場合 (推奨)を選択し、
マッピングテンプレート > マッピングテンプレートの追加を選択

コンテンツタイプに
application/json

テンプレート本文に下記のように記入して保存を選択してください。

{
  "body" : $input.json('$')
}

これによって元々のリクエスト本文を"body"というキーでラップした新しいJSONオブジェクトが生成されます。
例えば、クライアントが以下のようなJSON形式のリクエストを送信したとします。

{
  "name": "John",
  "age": 30
}

このリクエストがAPI Gatewayに送信され、統合リクエストのマッピングテンプレートで上記のテンプレートを適用したとします。この場合は、マッピングテンプレートによってリクエスト本文を以下のように変換されるということです。
この設定をしない場合、リクエストボディはそのままバックエンドサービスに転送されます。つまり、クライアントから受け取ったJSONデータが変換されずにそのままバックエンドへ送信されます。

{
  "body": {
    "name": "John",
    "age": 30
  }
}

CORSの有効化

リソース > CORSを有効にするを選択

Access-Control-Allow-MethodsでPOSTにチェックを入れ、保存を選択してください。

APIをデプロイ

APIをデプロイを選択

ステージは*新しいデプロイ*選択、ステージ名を入力し、デプロイを選択してください。

URLを呼び出す(黒枠の部分)に記載しているものをエンドポイントとして.envにエンドポイントを登録して使用してください。

.envにエンドポイントを登録する

下記記事の目次の**.env**を参考にしてください。
https://zenn.dev/nenenemo/articles/082ac7dcffe308

テスト

リソース > POSTを選択

リクエスト本文に下記を入力し、テストを選択してください。

{"password":"a"}

下記のように表示されるはずです。

上記までの設定が終了すると問題なく、完成イメージのような動作になるはずです。
以下はCloudWatchを使用したログの確認方法、LambdaにHTTPSエンドポイントを追加する方法になります。

CloudWatch Logsにログを出力する

ロールの作成

ユースケースはAPI Gatewayを選択して、次へ

AmazonAPIGatewayPushToCloudWatchLogsが許可ポリシーに記載してあることを確認して次へを選択

ロール名を入力してロールを作成を選択してください。

CloudWatch Logs への書き込み権限をもつ IAM ロールを設定する

API Gateway > 設定 > ログ記録の編集を選択

先ほど作成したロールのARNを貼り付けて、変更を保存を選択してください。

API Gateway CloudWatch ログを有効化

ステージをログとトレースの編集を選択

CloudWatch ログからログに残したい内容を選択し、変更を保存を選択してください。

LambdaにHTTPSエンドポイントを追加する

Lambda関数はコードを実行するためのサーバーレスコンピューティングプラットフォームであり、HTTPSリクエストを直接処理するものではありません。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/urls-configuration.html

下記手順に従ってHTTPSエンドポイントを追加してください。

設定を選択

左にあるメニューの関数URLを選択

関数URLを作成を選択

NONEにチェックを入れてください。

オリジン間リソース共有 (CORS) を設定にチェックを入れて
許可オリジンにはhttps://*と入力(ワイルドカードを使用してすべてのサブドメインを許可するための設定です。)
許可メソッドでPOSTを選択し、保存を選択してください。

関数URL(エンドポイント)が作成されました。

終わりに

何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉

Discussion